فهرست منبع

Merge pull request #1280 from porter-dev/master

Invalid invite token fix + metrics -> staging
abelanger5 4 سال پیش
والد
کامیت
63ca669fa0

+ 13 - 0
.github/workflows/test-backend.yml

@@ -0,0 +1,13 @@
+name: Backend CI
+on:
+  - pull_request
+jobs:
+  backend-tests:
+    name: Run Go tests
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: actions/setup-go@v2.1.4
+      with:
+        go-version: '^1.15.1'
+    - run: go test ./...

+ 11 - 0
api/server/handlers/infra/delete.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/models"
@@ -37,6 +38,16 @@ func (c *InfraDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
+	if infra.Kind == types.InfraDOKS || infra.Kind == types.InfraGKE || infra.Kind == types.InfraEKS {
+		c.Config().AnalyticsClient.Track(analytics.ClusterDestroyingStartTrack(
+			&analytics.ClusterDestroyingStartTrackOpts{
+				ClusterScopedTrackOpts: analytics.GetClusterScopedTrackOpts(infra.CreatedByUserID, infra.ProjectID, 0),
+				ClusterType:            infra.Kind,
+				InfraID:                infra.ID,
+			},
+		))
+	}
+
 	infra.Status = types.StatusDestroying
 	infra.Status = types.StatusDestroying
 	infra, err := c.Repo().Infra().UpdateInfra(infra)
 	infra, err := c.Repo().Infra().UpdateInfra(infra)
 
 

+ 17 - 30
api/server/handlers/invite/accept.go

@@ -1,12 +1,10 @@
 package invite
 package invite
 
 
 import (
 import (
+	"errors"
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
 	"net/url"
 	"net/url"
-	"strconv"
-
-	"github.com/go-chi/chi"
 
 
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
@@ -14,6 +12,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/requestutils"
 	"github.com/porter-dev/porter/api/server/shared/requestutils"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/models"
+	"gorm.io/gorm"
 )
 )
 
 
 type InviteAcceptHandler struct {
 type InviteAcceptHandler struct {
@@ -29,33 +28,28 @@ func NewInviteAcceptHandler(
 }
 }
 
 
 func (c *InviteAcceptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 func (c *InviteAcceptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
+	projectID, _ := requestutils.GetURLParamUint(r, types.URLParamProjectID)
 	token, _ := requestutils.GetURLParamString(r, types.URLParamInviteToken)
 	token, _ := requestutils.GetURLParamString(r, types.URLParamInviteToken)
 
 
-	session, err := c.Config().Store.Get(r, c.Config().ServerConf.CookieName)
-
-	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-	}
-
-	userID, _ := session.Values["user_id"].(uint)
-
-	user, err := c.Repo().User().ReadUser(userID)
+	proj, err := c.Repo().Project().ReadProject(projectID)
 
 
 	if err != nil {
 	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
+		vals := url.Values{}
 
 
-	projectID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			vals.Add("error", "Invalid invite token")
+		} else {
+			vals.Add("error", "Unknown error")
+		}
 
 
-	if err != nil || projectID == 0 {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		http.Redirect(w, r, fmt.Sprintf("/dashboard?%s", vals.Encode()), 302)
 		return
 		return
 	}
 	}
 
 
 	invite, err := c.Repo().Invite().ReadInviteByToken(token)
 	invite, err := c.Repo().Invite().ReadInviteByToken(token)
 
 
-	if err != nil || invite.ProjectID != uint(projectID) {
+	if err != nil || invite.ProjectID != proj.ID {
 		vals := url.Values{}
 		vals := url.Values{}
 		vals.Add("error", "Invalid invite token")
 		vals.Add("error", "Invalid invite token")
 		http.Redirect(w, r, fmt.Sprintf("/dashboard?%s", vals.Encode()), 302)
 		http.Redirect(w, r, fmt.Sprintf("/dashboard?%s", vals.Encode()), 302)
@@ -87,17 +81,10 @@ func (c *InviteAcceptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		kind = models.RoleDeveloper
 		kind = models.RoleDeveloper
 	}
 	}
 
 
-	project, err := c.Repo().Project().ReadProject(uint(projectID))
-
-	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
-
-	if _, err = c.Repo().Project().CreateProjectRole(project, &models.Role{
+	if _, err = c.Repo().Project().CreateProjectRole(proj, &models.Role{
 		Role: types.Role{
 		Role: types.Role{
-			UserID:    userID,
-			ProjectID: project.ID,
+			UserID:    user.ID,
+			ProjectID: proj.ID,
 			Kind:      types.RoleKind(kind),
 			Kind:      types.RoleKind(kind),
 		},
 		},
 	}); err != nil {
 	}); err != nil {
@@ -106,7 +93,7 @@ func (c *InviteAcceptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	}
 	}
 
 
 	// update the invite
 	// update the invite
-	invite.UserID = userID
+	invite.UserID = user.ID
 
 
 	if _, err = c.Repo().Invite().UpdateInvite(invite); err != nil {
 	if _, err = c.Repo().Invite().UpdateInvite(invite); err != nil {
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))

+ 2 - 0
api/server/router/invite.go

@@ -116,6 +116,8 @@ func getInviteRoutes(
 				Parent:       basePath,
 				Parent:       basePath,
 				RelativePath: "/invites/{token}",
 				RelativePath: "/invites/{token}",
 			},
 			},
+			// only user scope is needed here. adding the project scope will prevent the user
+			// from joining the project, since they don't have a role in the project yet.
 			Scopes: []types.PermissionScope{
 			Scopes: []types.PermissionScope{
 				types.UserScope,
 				types.UserScope,
 			},
 			},

+ 4 - 0
internal/analytics/track_events.go

@@ -30,4 +30,8 @@ const (
 	ApplicationLaunchSuccess SegmentEvent = "Application Launch Success"
 	ApplicationLaunchSuccess SegmentEvent = "Application Launch Success"
 
 
 	ApplicationDeploymentWebhook SegmentEvent = "Triggered Re-deploy via Webhook"
 	ApplicationDeploymentWebhook SegmentEvent = "Triggered Re-deploy via Webhook"
+
+	// delete events
+	ClusterDestroyingStart   SegmentEvent = "Cluster Destroying Start"
+	ClusterDestroyingSuccess SegmentEvent = "Cluster Destroying Success"
 )
 )

+ 44 - 0
internal/analytics/tracks.go

@@ -442,3 +442,47 @@ func RegistryProvisioningSuccessTrack(opts *RegistryProvisioningSuccessTrackOpts
 		getDefaultSegmentTrack(additionalProps, RegistryProvisioningSuccess),
 		getDefaultSegmentTrack(additionalProps, RegistryProvisioningSuccess),
 	)
 	)
 }
 }
+
+// ClusterDestroyingStartTrackOpts are the options for creating a track when a cluster
+// has started destroying
+type ClusterDestroyingStartTrackOpts struct {
+	*ClusterScopedTrackOpts
+
+	ClusterType types.InfraKind
+	InfraID     uint
+}
+
+// ClusterDestroyingStartTrack returns a track for when a cluster
+// has started destroying
+func ClusterDestroyingStartTrack(opts *ClusterDestroyingStartTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+	additionalProps["cluster_type"] = opts.ClusterType
+	additionalProps["infra_id"] = opts.InfraID
+
+	return getSegmentClusterTrack(
+		opts.ClusterScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, ClusterDestroyingStart),
+	)
+}
+
+// ClusterDestroyingSuccessTrackOpts are the options for creating a track when a cluster
+// has successfully provisioned
+type ClusterDestroyingSuccessTrackOpts struct {
+	*ClusterScopedTrackOpts
+
+	ClusterType types.InfraKind
+	InfraID     uint
+}
+
+// ClusterDestroyingSuccessTrack returns a new track for when a cluster
+// has successfully provisioned
+func ClusterDestroyingSuccessTrack(opts *ClusterDestroyingSuccessTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+	additionalProps["cluster_type"] = opts.ClusterType
+	additionalProps["infra_id"] = opts.InfraID
+
+	return getSegmentClusterTrack(
+		opts.ClusterScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, ClusterDestroyingSuccess),
+	)
+}

+ 10 - 0
internal/kubernetes/provisioner/global_stream.go

@@ -402,6 +402,16 @@ func GlobalStreamListener(
 				if err != nil {
 				if err != nil {
 					continue
 					continue
 				}
 				}
+
+				if infra.Kind == types.InfraDOKS || infra.Kind == types.InfraGKE || infra.Kind == types.InfraEKS {
+					analyticsClient.Track(analytics.ClusterDestroyingSuccessTrack(
+						&analytics.ClusterDestroyingSuccessTrackOpts{
+							ClusterScopedTrackOpts: analytics.GetClusterScopedTrackOpts(infra.CreatedByUserID, infra.ProjectID, 0),
+							ClusterType:            infra.Kind,
+							InfraID:                infra.ID,
+						},
+					))
+				}
 			}
 			}
 
 
 			// acknowledge the message as read
 			// acknowledge the message as read