Browse Source

add delete current user endpoint

Alexander Belanger 4 năm trước cách đây
mục cha
commit
722d3ab08e

+ 51 - 0
api/server/authn/session_helpers.go

@@ -0,0 +1,51 @@
+package authn
+
+import (
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+func SaveUserAuthenticated(
+	w http.ResponseWriter,
+	r *http.Request,
+	config *shared.Config,
+	user *models.User,
+) error {
+	session, err := config.Store.Get(r, config.CookieName)
+
+	if err != nil {
+		return err
+	}
+
+	var redirect string
+
+	if valR := session.Values["redirect"]; valR != nil {
+		redirect = session.Values["redirect"].(string)
+	}
+
+	session.Values["authenticated"] = true
+	session.Values["user_id"] = user.ID
+	session.Values["email"] = user.Email
+	session.Values["redirect"] = redirect
+
+	return session.Save(r, w)
+}
+
+func SaveUserUnauthenticated(
+	w http.ResponseWriter,
+	r *http.Request,
+	config *shared.Config,
+) error {
+	session, err := config.Store.Get(r, config.CookieName)
+
+	if err != nil {
+		return err
+	}
+
+	session.Values["authenticated"] = false
+	session.Values["user_id"] = nil
+	session.Values["email"] = nil
+	return session.Save(r, w)
+}

+ 3 - 19
api/server/handlers/user/create.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"net/http"
 
+	"github.com/porter-dev/porter/api/server/authn"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/types"
@@ -62,25 +63,8 @@ func (u *UserCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	session, err := u.config.Store.Get(r, u.config.CookieName)
-
-	if err != nil {
-		apierrors.HandleAPIError(w, u.config.Logger, apierrors.NewErrInternal(err))
-		return
-	}
-
-	var redirect string
-
-	if valR := session.Values["redirect"]; valR != nil {
-		redirect = session.Values["redirect"].(string)
-	}
-
-	session.Values["authenticated"] = true
-	session.Values["user_id"] = user.ID
-	session.Values["email"] = user.Email
-	session.Values["redirect"] = redirect
-
-	if err := session.Save(r, w); err != nil {
+	// save the user as authenticated in the session
+	if err := authn.SaveUserAuthenticated(w, r, u.config, user); err != nil {
 		apierrors.HandleAPIError(w, u.config.Logger, apierrors.NewErrInternal(err))
 		return
 	}

+ 42 - 0
api/server/handlers/user/delete.go

@@ -0,0 +1,42 @@
+package user
+
+import (
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/authn"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type UserDeleteHandler struct {
+	config *shared.Config
+	writer shared.ResultWriter
+}
+
+func NewUserDeleteHandler(
+	config *shared.Config,
+	writer shared.ResultWriter,
+) *UserDeleteHandler {
+	return &UserDeleteHandler{config, writer}
+}
+
+func (u *UserDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	user, _ := r.Context().Value(types.UserScope).(*models.User)
+
+	user, err := u.config.Repo.User().DeleteUser(user)
+
+	if err != nil {
+		apierrors.HandleAPIError(w, u.config.Logger, apierrors.NewErrInternal(err))
+		return
+	}
+
+	// set the user as unauthenticated in the session
+	if err := authn.SaveUserUnauthenticated(w, r, u.config); err != nil {
+		apierrors.HandleAPIError(w, u.config.Logger, apierrors.NewErrInternal(err))
+		return
+	}
+
+	u.writer.WriteResult(w, user.ToUserType())
+}

+ 72 - 0
api/server/handlers/user/delete_test.go

@@ -0,0 +1,72 @@
+package user_test
+
+import (
+	"testing"
+
+	"github.com/porter-dev/porter/api/server/handlers/user"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apitest"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/repository/test"
+	"github.com/stretchr/testify/assert"
+	"gorm.io/gorm"
+)
+
+func TestDeleteUserSuccessful(t *testing.T) {
+	req, rr := apitest.GetRequestAndRecorder(
+		t,
+		string(types.HTTPVerbDelete),
+		"/api/users/current",
+		nil,
+	)
+
+	config := apitest.LoadConfig(t)
+	authUser := apitest.CreateTestUser(t, config)
+	req = apitest.WithAuthenticatedUser(t, req, authUser)
+
+	handler := user.NewUserDeleteHandler(
+		config,
+		shared.NewDefaultResultWriter(config),
+	)
+
+	handler.ServeHTTP(rr, req)
+
+	expUser := &types.CreateUserResponse{
+		ID:            1,
+		Email:         "test@test.it",
+		EmailVerified: true,
+	}
+
+	gotUser := &types.CreateUserResponse{}
+
+	apitest.AssertResponseExpected(t, rr, expUser, gotUser)
+
+	// assert that the user has been deleted
+	authUser, err := config.Repo.User().ReadUser(1)
+
+	targetErr := gorm.ErrRecordNotFound
+
+	assert.ErrorIs(t, err, targetErr)
+}
+
+func TestFailingDeleteUserMethod(t *testing.T) {
+	req, rr := apitest.GetRequestAndRecorder(
+		t,
+		string(types.HTTPVerbDelete),
+		"/api/users/current",
+		nil,
+	)
+
+	config := apitest.LoadConfig(t, test.DeleteUserMethod)
+	authUser := apitest.CreateTestUser(t, config)
+	req = apitest.WithAuthenticatedUser(t, req, authUser)
+
+	handler := user.NewUserDeleteHandler(
+		config,
+		shared.NewDefaultResultWriter(config),
+	)
+
+	handler.ServeHTTP(rr, req)
+
+	apitest.AssertResponseInternalServerError(t, rr)
+}

+ 24 - 0
api/server/router/user.go

@@ -67,6 +67,30 @@ func getUserRoutes(
 		Router:   r,
 	})
 
+	// DELETE /api/users/current -> user.NewUserDeleteHandler
+	deleteUserEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbDelete,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: "/users/current",
+			},
+			Scopes: []types.PermissionScope{types.UserScope},
+		},
+	)
+
+	deleteUserHandler := user.NewUserDeleteHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: deleteUserEndpoint,
+		Handler:  deleteUserHandler,
+		Router:   r,
+	})
+
 	// POST /api/projects -> project.NewProjectCreateHandler
 	createEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 2 - 1
internal/repository/test/user.go

@@ -12,6 +12,7 @@ import (
 
 const (
 	CreateUserMethod string = "create_user_0"
+	DeleteUserMethod string = "delete_user_0"
 )
 
 // UserRepository will return errors on queries if canQuery is false
@@ -128,7 +129,7 @@ func (repo *UserRepository) UpdateUser(user *models.User) (*models.User, error)
 
 // DeleteUser deletes a single user using their unique id
 func (repo *UserRepository) DeleteUser(user *models.User) (*models.User, error) {
-	if !repo.canQuery {
+	if !repo.canQuery || strings.Contains(repo.failingMethods, DeleteUserMethod) {
 		return nil, errors.New("Cannot write database")
 	}