From ba19afcb7ddf3810ad751db1c0ac5f766fce7847 Mon Sep 17 00:00:00 2001 From: Vladimir Pestov Date: Mon, 30 Oct 2023 21:21:35 +0500 Subject: [PATCH 1/2] fix: Remove first and last name from user, move requests to different file --- internal/pgdb/client/client.go | 11 ++-- .../V0002__RemoveUserFirstLastName.sql | 1 + pkg/storage/models.go | 28 ---------- pkg/storage/requests.go | 56 +++++++++++++++++++ 4 files changed, 63 insertions(+), 33 deletions(-) create mode 100644 internal/pgdb/migrations/V0002__RemoveUserFirstLastName.sql create mode 100644 pkg/storage/requests.go diff --git a/internal/pgdb/client/client.go b/internal/pgdb/client/client.go index c294ad9..a677404 100644 --- a/internal/pgdb/client/client.go +++ b/internal/pgdb/client/client.go @@ -37,10 +37,6 @@ func (c *Client) CreateUser(ctx context.Context, req *storage.CreateUserRequest) Columns("username", "password"). Suffix("RETURNING id"). PlaceholderFormat(sq.Dollar) - if req.FirstName != "" && req.LastName != "" { - query = query.Columns("first_name", "last_name") - values = append(values, req.FirstName, req.LastName) - } if req.AvatarURL != "" { query = query.Columns("avatar_url") values = append(values, req.AvatarURL) @@ -64,7 +60,12 @@ func (c *Client) CreateUser(ctx context.Context, req *storage.CreateUserRequest) if err := row.Scan(&id); err != nil { return nil, xerrors.Errorf("failed to scan row: %w", err) } - user := storage.User(*req) + user := storage.User{ + Id: id, + Username: req.Username, + Password: req.Password, + AvatarURL: req.AvatarURL, + } user.Id = id return &user, nil } diff --git a/internal/pgdb/migrations/V0002__RemoveUserFirstLastName.sql b/internal/pgdb/migrations/V0002__RemoveUserFirstLastName.sql new file mode 100644 index 0000000..2bdb924 --- /dev/null +++ b/internal/pgdb/migrations/V0002__RemoveUserFirstLastName.sql @@ -0,0 +1 @@ +ALTER TABLE "user" DROP COLUMN first_name, DROP COLUMN last_name; \ No newline at end of file diff --git a/pkg/storage/models.go b/pkg/storage/models.go index 3656d45..180a61a 100644 --- a/pkg/storage/models.go +++ b/pkg/storage/models.go @@ -39,16 +39,6 @@ type Quest struct { MaxTeamCap *int } -type CreateQuestRequest Quest - -type GetQuestRequest struct { - Id string -} - -type UpdateQuestRequest Quest - -type DeleteQuestRequest GetQuestRequest - type Team struct { Id string Name string @@ -58,25 +48,7 @@ type Team struct { InviteLink string } -type CreateTeamRequest Team - type User struct { - Id string `json:"id"` - Username string `json:"username"` - Password string `json:"password,omitempty"` - FirstName string `json:"first_name,omitempty"` - LastName string `json:"last_name,omitempty"` - AvatarURL string `json:"avatar_url,omitempty"` -} - -type CreateUserRequest User - -type GetUserRequest struct { - Id string - Username string -} - -type UpdateUserRequest struct { Id string `json:"id"` Username string `json:"username"` Password string `json:"password,omitempty"` diff --git a/pkg/storage/requests.go b/pkg/storage/requests.go new file mode 100644 index 0000000..7fbaf23 --- /dev/null +++ b/pkg/storage/requests.go @@ -0,0 +1,56 @@ +package storage + +import "time" + +type CreateUserRequest struct { + Username string `json:"username"` + Password string `json:"password,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` +} + +type GetUserRequest struct { + Id string + Username string +} + +type UpdateUserRequest struct { + Id string `json:"id"` + Username string `json:"username"` + Password string `json:"password,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` +} + +type CreateQuestRequest struct { + Name string `json:"name"` + Description string `json:"description,omitempty"` + Access AccessType `json:"access"` + CreatorName string `json:"creator_name"` + Creator *User `json:"-"` + RegistrationDeadline *time.Time `json:"registration_deadline"` + StartTime *time.Time `json:"start_time"` + FinishTime *time.Time `json:"finish_time"` + MediaLink string `json:"media_link"` + MaxTeamCap *int `json:"max_team_cap"` +} + +type GetQuestRequest struct { + Id string +} + +type UpdateQuestRequest struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + Access AccessType `json:"access"` + CreatorName string `json:"creator_name"` + Creator *User `json:"-"` + RegistrationDeadline *time.Time `json:"registration_deadline"` + StartTime *time.Time `json:"start_time"` + FinishTime *time.Time `json:"finish_time"` + MediaLink string `json:"media_link"` + MaxTeamCap *int `json:"max_team_cap"` +} + +type DeleteQuestRequest struct { + Id string +} From 09223c88e11f8cf1dc4b3120f1aa425ca4b98ffc Mon Sep 17 00:00:00 2001 From: Vladimir Pestov Date: Mon, 30 Oct 2023 21:44:22 +0500 Subject: [PATCH 2/2] feat: Calculate hash of password before storing it --- internal/handlers/user/create_handler.go | 9 ++++++++- internal/handlers/user/create_handler_test.go | 16 ++++++++++++---- main.go | 4 +++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/internal/handlers/user/create_handler.go b/internal/handlers/user/create_handler.go index 9e2cf6d..2e1b621 100644 --- a/internal/handlers/user/create_handler.go +++ b/internal/handlers/user/create_handler.go @@ -3,6 +3,7 @@ package user import ( "encoding/json" "errors" + "hash" "net/http" "questspace/internal/validate" aerrors "questspace/pkg/application/errors" @@ -18,15 +19,20 @@ const defaultAvatarURL = "https://api.dicebear.com/7.x/thumbs/svg" type CreateHandler struct { storage storage.UserStorage fetcher http.Client + hasher hash.Hash } -func NewCreateHandler(s storage.UserStorage, f http.Client) CreateHandler { +func NewCreateHandler(s storage.UserStorage, f http.Client, h hash.Hash) CreateHandler { return CreateHandler{ storage: s, fetcher: f, + hasher: h, } } +// @Param request body storage.CreateUserRequest true "query params" +// @Success 200 {object} storage.User +// @Router /user [post] func (h CreateHandler) Handle(c *gin.Context) error { data, err := c.GetRawData() if err != nil { @@ -42,6 +48,7 @@ func (h CreateHandler) Handle(c *gin.Context) error { if req.AvatarURL == "" { req.AvatarURL = defaultAvatarURL } + req.Password = string(h.hasher.Sum([]byte(req.Password))) user, err := h.storage.CreateUser(c, &req) if err != nil { if errors.Is(err, storage.ErrExists) { diff --git a/internal/handlers/user/create_handler_test.go b/internal/handlers/user/create_handler_test.go index cd78db3..f7c87f5 100644 --- a/internal/handlers/user/create_handler_test.go +++ b/internal/handlers/user/create_handler_test.go @@ -2,6 +2,7 @@ package user import ( "bytes" + "crypto/sha256" "encoding/json" "net/http" "net/http/httptest" @@ -86,7 +87,8 @@ func TestCreateHandler_CommonCases(t *testing.T) { ctrl := gomock.NewController(t) userStorage := mocks.NewMockUserStorage(ctrl) router := gin.Default() - handler := NewCreateHandler(userStorage, http.Client{}) + hasher := sha256.New() + handler := NewCreateHandler(userStorage, http.Client{}, hasher) router.POST("/test", application.AsGinHandler(handler.Handle)) for _, tc := range testCases { @@ -105,9 +107,14 @@ func TestCreateHandler_CommonCases(t *testing.T) { require.NoError(t, err) request, err := http.NewRequest(http.MethodPost, "/test", bytes.NewReader(raw)) require.NoError(t, err) + actualReq := &storage.CreateUserRequest{ + Username: tc.req.Username, + Password: string(hasher.Sum([]byte(tc.req.Password))), + AvatarURL: tc.req.AvatarURL, + } if tc.wantStore { - userStorage.EXPECT().CreateUser(gomock.Any(), tc.req).Return(nil, tc.storeErr) + userStorage.EXPECT().CreateUser(gomock.Any(), actualReq).Return(nil, tc.storeErr) } router.ServeHTTP(rr, request) @@ -120,9 +127,10 @@ func TestCreateHandler_SetsDefaultURL(t *testing.T) { gin.SetMode(gin.TestMode) ctrl := gomock.NewController(t) userStorage := mocks.NewMockUserStorage(ctrl) + hasher := sha256.New() rr := httptest.NewRecorder() router := gin.Default() - handler := NewCreateHandler(userStorage, http.Client{}) + handler := NewCreateHandler(userStorage, http.Client{}, hasher) router.POST("/test", application.AsGinHandler(handler.Handle)) req := &storage.CreateUserRequest{ @@ -131,7 +139,7 @@ func TestCreateHandler_SetsDefaultURL(t *testing.T) { } storageReq := &storage.CreateUserRequest{ Username: "user", - Password: "password", + Password: string(hasher.Sum([]byte("password"))), AvatarURL: defaultAvatarURL, } diff --git a/main.go b/main.go index a808f94..62c431f 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "crypto/sha256" "fmt" "net/http" user "questspace/internal/handlers/user" @@ -70,10 +71,11 @@ func Init(app application.App) error { } sqlStorage := pgdb.NewClient(conn) client := http.Client{} + hasher := sha256.New() userGroup := app.Router().Group("/user") - createHandler := user.NewCreateHandler(sqlStorage, client) + createHandler := user.NewCreateHandler(sqlStorage, client, hasher) userGroup.POST("", application.AsGinHandler(createHandler.Handle)) getHandler := user.NewGetHandler(sqlStorage)