From d05b50136028bc4b0fd5b871c764cb744d3aa4fb Mon Sep 17 00:00:00 2001 From: nochill Date: Mon, 13 Mar 2023 09:24:59 +0700 Subject: [PATCH] add user and password hash --- Makefile | 3 +- api/product_test.go | 2 +- api/server.go | 1 + api/user.go | 69 +++++++++++++++++ api/user_test.go | 1 + db/migrations/000001_init_schema.up.sql | 2 +- db/mock/store.go | 75 ++++++++++++++++++ db/query/{merchant.sql => merchants.sql} | 5 ++ db/query/users.sql | 6 +- db/sqlc/{merchant.sql.go => merchants.sql.go} | 27 ++++++- db/sqlc/merchants_test.go | 1 + db/sqlc/models.go | 14 ++-- db/sqlc/products_test.go | 2 +- db/sqlc/querier.go | 4 + db/sqlc/store.go | 44 +++++++++++ db/sqlc/store_test.go | 41 +++++++++- db/sqlc/suppliers_test.go | 2 +- db/sqlc/users.sql.go | 76 +++++++++++++++++++ db/sqlc/users_test.go | 7 ++ go.mod | 2 +- util/password.go | 19 +++++ util/password_test.go | 23 ++++++ util/random.go | 5 ++ 23 files changed, 410 insertions(+), 21 deletions(-) create mode 100644 api/user.go create mode 100644 api/user_test.go rename db/query/{merchant.sql => merchants.sql} (60%) rename db/sqlc/{merchant.sql.go => merchants.sql.go} (62%) create mode 100644 db/sqlc/merchants_test.go create mode 100644 db/sqlc/users.sql.go create mode 100644 db/sqlc/users_test.go create mode 100644 util/password.go create mode 100644 util/password_test.go diff --git a/Makefile b/Makefile index 474e7dd..766c817 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,8 @@ migratedown: mock-generate: mockgen -package mockdb -destination db/mock/store.go git.nochill.in/nochill/naice_pos/db/sqlc Store - sqlc: - sqlc generate + sqlc generate && make mock-generate test: go test -v -cover ./... diff --git a/api/product_test.go b/api/product_test.go index 541ada2..19b23d2 100644 --- a/api/product_test.go +++ b/api/product_test.go @@ -109,7 +109,7 @@ func TestGetProductApi(t *testing.T) { func randomProduct() db.Product { return db.Product{ ID: uuid.New(), - MerchantID: uuid.MustParse("d9b0e126-991e-46ac-8c61-5efdd605f75d"), + MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"), Name: util.RandomString(5), SellingPrice: util.RandomFloat(1000, 99999), PurchasePrice: util.RandomFloat(999, 9999), diff --git a/api/server.go b/api/server.go index e29a107..32d9614 100644 --- a/api/server.go +++ b/api/server.go @@ -14,6 +14,7 @@ func NewServer(store db.Store) *Server { server := &Server{store: store} router := gin.Default() + router.POST("/merchants", server.createUser) router.POST("/products", server.createProduct) router.PATCH("/products", server.updateProduct) router.GET("/product/:id", server.getProduct) diff --git a/api/user.go b/api/user.go new file mode 100644 index 0000000..1451e63 --- /dev/null +++ b/api/user.go @@ -0,0 +1,69 @@ +package api + +import ( + "database/sql" + "net/http" + + db "git.nochill.in/nochill/naice_pos/db/sqlc" + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/lib/pq" +) + +type createUserRequest struct { + Email string `json:"email" binding:"required,email"` + Fullname string `json:"fullname" binding:"required"` + Password string `json:"password" binding:"required"` + OutletName string `json:"outlet_name" binding:"required"` +} + +type createUserResponse struct { + ID uuid.UUID `json:"id"` + IndexID int64 `json:"index_id"` + Email string `json:"email"` + Fullname string `json:"fullname"` + CreatedAt sql.NullTime `json:"created_at"` + UpdatedAt sql.NullTime `json:"updated_at"` + Outlet db.Merchant `json:"outlet"` +} + +func (server *Server) createUser(ctx *gin.Context) { + var req createUserRequest + + if err := ctx.ShouldBindJSON(&req); err != nil { + ctx.JSON(http.StatusBadRequest, errorResponse(err)) + return + } + + arg := db.UserMerchantTxParams{ + Email: req.Email, + Fullname: req.Fullname, + Password: req.Password, + OutletName: req.OutletName, + } + + user, err := server.store.CreateUserMerchantTx(ctx, arg) + if err != nil { + if pqErr, ok := err.(*pq.Error); ok { + switch pqErr.Code.Name() { + case "foreign_key_violation", "unique_violation": + ctx.JSON(http.StatusConflict, errorResponse(err)) + return + } + } + ctx.JSON(http.StatusInternalServerError, errorResponse(err)) + return + } + + res := createUserResponse{ + ID: user.OwnerProfile.ID, + IndexID: user.OwnerProfile.IndexID, + Email: user.OwnerProfile.Email, + Fullname: user.OwnerProfile.Fullname, + CreatedAt: sql.NullTime{Valid: true, Time: user.OutletProfile.CreatedAt.Time}, + UpdatedAt: sql.NullTime{Valid: true, Time: user.OutletProfile.UpdatedAt.Time}, + Outlet: user.OutletProfile, + } + + ctx.JSON(http.StatusOK, res) +} diff --git a/api/user_test.go b/api/user_test.go new file mode 100644 index 0000000..778f64e --- /dev/null +++ b/api/user_test.go @@ -0,0 +1 @@ +package api diff --git a/db/migrations/000001_init_schema.up.sql b/db/migrations/000001_init_schema.up.sql index 07f791e..7ae43ca 100644 --- a/db/migrations/000001_init_schema.up.sql +++ b/db/migrations/000001_init_schema.up.sql @@ -3,7 +3,7 @@ CREATE TABLE users( "index_id" bigserial not null, "email" varchar unique not null, "password" varchar not null, - "fullname" varchar, + "fullname" varchar not null, "created_at" timestamp default(now()), "updated_at" timestamp default(now()) ); diff --git a/db/mock/store.go b/db/mock/store.go index 1a07171..5372179 100644 --- a/db/mock/store.go +++ b/db/mock/store.go @@ -51,6 +51,21 @@ func (mr *MockStoreMockRecorder) CreateCustomers(arg0, arg1 interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCustomers", reflect.TypeOf((*MockStore)(nil).CreateCustomers), arg0, arg1) } +// CreateMerchant mocks base method. +func (m *MockStore) CreateMerchant(arg0 context.Context, arg1 db.CreateMerchantParams) (db.Merchant, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateMerchant", arg0, arg1) + ret0, _ := ret[0].(db.Merchant) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateMerchant indicates an expected call of CreateMerchant. +func (mr *MockStoreMockRecorder) CreateMerchant(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateMerchant", reflect.TypeOf((*MockStore)(nil).CreateMerchant), arg0, arg1) +} + // CreateProduct mocks base method. func (m *MockStore) CreateProduct(arg0 context.Context, arg1 db.CreateProductParams) (db.Product, error) { m.ctrl.T.Helper() @@ -111,6 +126,36 @@ func (mr *MockStoreMockRecorder) CreateSuppliers(arg0, arg1 interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSuppliers", reflect.TypeOf((*MockStore)(nil).CreateSuppliers), arg0, arg1) } +// CreateUser mocks base method. +func (m *MockStore) CreateUser(arg0 context.Context, arg1 db.CreateUserParams) (db.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateUser", arg0, arg1) + ret0, _ := ret[0].(db.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateUser indicates an expected call of CreateUser. +func (mr *MockStoreMockRecorder) CreateUser(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockStore)(nil).CreateUser), arg0, arg1) +} + +// CreateUserMerchantTx mocks base method. +func (m *MockStore) CreateUserMerchantTx(arg0 context.Context, arg1 db.UserMerchantTxParams) (db.UserMerchantTxResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateUserMerchantTx", arg0, arg1) + ret0, _ := ret[0].(db.UserMerchantTxResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateUserMerchantTx indicates an expected call of CreateUserMerchantTx. +func (mr *MockStoreMockRecorder) CreateUserMerchantTx(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUserMerchantTx", reflect.TypeOf((*MockStore)(nil).CreateUserMerchantTx), arg0, arg1) +} + // CustomersList mocks base method. func (m *MockStore) CustomersList(arg0 context.Context, arg1 db.CustomersListParams) ([]db.Customer, error) { m.ctrl.T.Helper() @@ -198,6 +243,21 @@ func (mr *MockStoreMockRecorder) GetMerchantByUserId(arg0, arg1 interface{}) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMerchantByUserId", reflect.TypeOf((*MockStore)(nil).GetMerchantByUserId), arg0, arg1) } +// GetPasswordByEmail mocks base method. +func (m *MockStore) GetPasswordByEmail(arg0 context.Context, arg1 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPasswordByEmail", arg0, arg1) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPasswordByEmail indicates an expected call of GetPasswordByEmail. +func (mr *MockStoreMockRecorder) GetPasswordByEmail(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPasswordByEmail", reflect.TypeOf((*MockStore)(nil).GetPasswordByEmail), arg0, arg1) +} + // GetProduct mocks base method. func (m *MockStore) GetProduct(arg0 context.Context, arg1 uuid.UUID) (db.Product, error) { m.ctrl.T.Helper() @@ -228,6 +288,21 @@ func (mr *MockStoreMockRecorder) GetStockForUpdateStock(arg0, arg1 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStockForUpdateStock", reflect.TypeOf((*MockStore)(nil).GetStockForUpdateStock), arg0, arg1) } +// GetUserById mocks base method. +func (m *MockStore) GetUserById(arg0 context.Context, arg1 uuid.UUID) (db.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUserById", arg0, arg1) + ret0, _ := ret[0].(db.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUserById indicates an expected call of GetUserById. +func (mr *MockStoreMockRecorder) GetUserById(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserById", reflect.TypeOf((*MockStore)(nil).GetUserById), arg0, arg1) +} + // ListProducts mocks base method. func (m *MockStore) ListProducts(arg0 context.Context, arg1 db.ListProductsParams) ([]db.Product, error) { m.ctrl.T.Helper() diff --git a/db/query/merchant.sql b/db/query/merchants.sql similarity index 60% rename from db/query/merchant.sql rename to db/query/merchants.sql index 61fde98..46bd76b 100644 --- a/db/query/merchant.sql +++ b/db/query/merchants.sql @@ -1,3 +1,8 @@ +-- name: CreateMerchant :one +INSERT INTO merchants (name,owner_id) +VALUES ($1, $2) +RETURNING *; + -- name: GetMerchantById :one SELECT * FROM merchants WHERE id = $1; diff --git a/db/query/users.sql b/db/query/users.sql index 514c717..6ef5463 100644 --- a/db/query/users.sql +++ b/db/query/users.sql @@ -9,11 +9,9 @@ RETURNING *; -- name: GetPasswordByEmail :one SELECT password FROM users -WHERE email = $1 -RETURNING password; +WHERE email = $1; -- name: GetUserById :one SELECT * FROM users -WHERE id = $1 -RETURNING *; \ No newline at end of file +WHERE id = $1; \ No newline at end of file diff --git a/db/sqlc/merchant.sql.go b/db/sqlc/merchants.sql.go similarity index 62% rename from db/sqlc/merchant.sql.go rename to db/sqlc/merchants.sql.go index f796c5f..deaf425 100644 --- a/db/sqlc/merchant.sql.go +++ b/db/sqlc/merchants.sql.go @@ -1,7 +1,7 @@ // Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.17.2 -// source: merchant.sql +// source: merchants.sql package db @@ -11,6 +11,31 @@ import ( "github.com/google/uuid" ) +const createMerchant = `-- name: CreateMerchant :one +INSERT INTO merchants (name,owner_id) +VALUES ($1, $2) +RETURNING id, index_id, name, owner_id, created_at, updated_at +` + +type CreateMerchantParams struct { + Name string `json:"name"` + OwnerID uuid.UUID `json:"owner_id"` +} + +func (q *Queries) CreateMerchant(ctx context.Context, arg CreateMerchantParams) (Merchant, error) { + row := q.db.QueryRowContext(ctx, createMerchant, arg.Name, arg.OwnerID) + var i Merchant + err := row.Scan( + &i.ID, + &i.IndexID, + &i.Name, + &i.OwnerID, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + const getMerchantById = `-- name: GetMerchantById :one SELECT id, index_id, name, owner_id, created_at, updated_at FROM merchants WHERE id = $1 diff --git a/db/sqlc/merchants_test.go b/db/sqlc/merchants_test.go new file mode 100644 index 0000000..3a49c63 --- /dev/null +++ b/db/sqlc/merchants_test.go @@ -0,0 +1 @@ +package db diff --git a/db/sqlc/models.go b/db/sqlc/models.go index 988f3f7..5195fb7 100644 --- a/db/sqlc/models.go +++ b/db/sqlc/models.go @@ -110,11 +110,11 @@ type Supplier struct { } type User struct { - ID uuid.UUID `json:"id"` - IndexID int64 `json:"index_id"` - Email string `json:"email"` - Password string `json:"password"` - Fullname sql.NullString `json:"fullname"` - CreatedAt sql.NullTime `json:"created_at"` - UpdatedAt sql.NullTime `json:"updated_at"` + ID uuid.UUID `json:"id"` + IndexID int64 `json:"index_id"` + Email string `json:"email"` + Password string `json:"password"` + Fullname string `json:"fullname"` + CreatedAt sql.NullTime `json:"created_at"` + UpdatedAt sql.NullTime `json:"updated_at"` } diff --git a/db/sqlc/products_test.go b/db/sqlc/products_test.go index 707f7b0..906d4a4 100644 --- a/db/sqlc/products_test.go +++ b/db/sqlc/products_test.go @@ -18,7 +18,7 @@ func createRandomProduct(t *testing.T) (Product, CreateProductParams) { stock := util.RandomFloat(10, 10000) arg := CreateProductParams{ - MerchantID: uuid.MustParse("d9b0e126-991e-46ac-8c61-5efdd605f75d"), + MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"), Name: util.RandomString(10), SellingPrice: sellingPrice, PurchasePrice: purchasePrice, diff --git a/db/sqlc/querier.go b/db/sqlc/querier.go index 9703e11..bea07fc 100644 --- a/db/sqlc/querier.go +++ b/db/sqlc/querier.go @@ -12,18 +12,22 @@ import ( type Querier interface { CreateCustomers(ctx context.Context, arg CreateCustomersParams) (Customer, error) + CreateMerchant(ctx context.Context, arg CreateMerchantParams) (Merchant, error) CreateProduct(ctx context.Context, arg CreateProductParams) (Product, error) CreatePurchaseOrder(ctx context.Context, arg CreatePurchaseOrderParams) (PurchaseOrder, error) CreatePurchaseOrderDetail(ctx context.Context, arg CreatePurchaseOrderDetailParams) (PurchaseOrderDetail, error) CreateSuppliers(ctx context.Context, arg CreateSuppliersParams) (Supplier, error) + CreateUser(ctx context.Context, arg CreateUserParams) (User, error) CustomersList(ctx context.Context, arg CustomersListParams) ([]Customer, error) DeleteCustomer(ctx context.Context, id uuid.UUID) error DeleteProduct(ctx context.Context, id uuid.UUID) error DeleteSupplier(ctx context.Context, id uuid.UUID) error GetMerchantById(ctx context.Context, id uuid.UUID) (Merchant, error) GetMerchantByUserId(ctx context.Context, ownerID uuid.UUID) (Merchant, error) + GetPasswordByEmail(ctx context.Context, email string) (string, error) GetProduct(ctx context.Context, id uuid.UUID) (Product, error) GetStockForUpdateStock(ctx context.Context, id uuid.UUID) (Product, error) + GetUserById(ctx context.Context, id uuid.UUID) (User, error) ListProducts(ctx context.Context, arg ListProductsParams) ([]Product, error) SuppliersList(ctx context.Context, arg SuppliersListParams) ([]Supplier, error) UpdateCustomer(ctx context.Context, id uuid.UUID) (Customer, error) diff --git a/db/sqlc/store.go b/db/sqlc/store.go index d14a9f7..a12e4a4 100644 --- a/db/sqlc/store.go +++ b/db/sqlc/store.go @@ -11,6 +11,7 @@ import ( type Store interface { Querier PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxParams) (PurchaseOrderTxResult, error) + CreateUserMerchantTx(ctx context.Context, arg UserMerchantTxParams) (UserMerchantTxResult, error) } type SQLStore struct { @@ -126,3 +127,46 @@ func (store *SQLStore) PurchaseOrderTx(ctx context.Context, arg PurchasoOrderTxP return result, err } + +type UserMerchantTxParams struct { + Email string `json:"email"` + Fullname string `json:"fullname"` + Password string `json:"password"` + OutletName string `json:"outlet_name"` +} + +type UserMerchantTxResult struct { + OwnerProfile User `json:"owner_profile"` + OutletProfile Merchant `json:"outlet_profile"` +} + +func (store *SQLStore) CreateUserMerchantTx(ctx context.Context, arg UserMerchantTxParams) (UserMerchantTxResult, error) { + var result UserMerchantTxResult + err := store.execTx(ctx, func(q *Queries) error { + var err error + + result.OwnerProfile, err = q.CreateUser(ctx, CreateUserParams{ + Email: arg.Email, + Password: arg.Password, + Fullname: arg.Fullname, + }) + + if err != nil { + return err + } + + result.OutletProfile, err = q.CreateMerchant(ctx, CreateMerchantParams{ + Name: arg.OutletName, + OwnerID: result.OwnerProfile.ID, + }) + + if err != nil { + return err + } + + return nil + }) + + return result, err + +} diff --git a/db/sqlc/store_test.go b/db/sqlc/store_test.go index b2ec985..e7a3e36 100644 --- a/db/sqlc/store_test.go +++ b/db/sqlc/store_test.go @@ -44,7 +44,9 @@ func TestPurchaseOrder(t *testing.T) { products = append(products, purchaseProducts_1, purchaseProducts_2) - for i := 0; i < 5; i++ { + tries := 3 + + for i := 0; i < tries; i++ { go func() { result, err := store.PurchaseOrderTx(context.Background(), PurchasoOrderTxParams{ MerchantID: supplier.MerchantID, @@ -61,7 +63,7 @@ func TestPurchaseOrder(t *testing.T) { }() } - for i := 0; i < 5; i++ { + for i := 0; i < tries; i++ { err := <-errs require.NoError(t, err) @@ -75,3 +77,38 @@ func TestPurchaseOrder(t *testing.T) { require.NotZero(t, purchaseOrder.PaidNominal, product1.PurchasePrice+product2.PurchasePrice) } } + +func TestCreateUserMerchant(t *testing.T) { + store := NewStore(testDB) + + errs := make(chan error) + results := make(chan UserMerchantTxResult) + + tries := 3 + + for i := 0; i < tries; i++ { + randString := util.RandomString(10) + password, err := util.HashPassword(randString) + require.NoError(t, err) + + go func() { + result, err := store.CreateUserMerchantTx(context.Background(), UserMerchantTxParams{ + Email: util.RandomEmail(), + Fullname: util.RandomString(6), + Password: password, + OutletName: util.RandomString(6), + }) + errs <- err + results <- result + }() + } + + for i := 0; i < tries; i++ { + err := <-errs + require.NoError(t, err) + + result := <-results + require.NotEmpty(t, result) + } + +} diff --git a/db/sqlc/suppliers_test.go b/db/sqlc/suppliers_test.go index e23bce4..6a6d361 100644 --- a/db/sqlc/suppliers_test.go +++ b/db/sqlc/suppliers_test.go @@ -11,7 +11,7 @@ import ( func createRandomSupplier(t *testing.T) (Supplier, CreateSuppliersParams) { arg := CreateSuppliersParams{ - MerchantID: uuid.MustParse("d9b0e126-991e-46ac-8c61-5efdd605f75d"), + MerchantID: uuid.MustParse("a848090f-0409-4386-9caa-929ae6874dbb"), Name: util.RandomString(10), } diff --git a/db/sqlc/users.sql.go b/db/sqlc/users.sql.go new file mode 100644 index 0000000..e79859f --- /dev/null +++ b/db/sqlc/users.sql.go @@ -0,0 +1,76 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.17.2 +// source: users.sql + +package db + +import ( + "context" + + "github.com/google/uuid" +) + +const createUser = `-- name: CreateUser :one +INSERT INTO users ( + email, + password, + fullname +) VALUES ($1, $2, $3) +RETURNING id, index_id, email, password, fullname, created_at, updated_at +` + +type CreateUserParams struct { + Email string `json:"email"` + Password string `json:"password"` + Fullname string `json:"fullname"` +} + +func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) { + row := q.db.QueryRowContext(ctx, createUser, arg.Email, arg.Password, arg.Fullname) + var i User + err := row.Scan( + &i.ID, + &i.IndexID, + &i.Email, + &i.Password, + &i.Fullname, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getPasswordByEmail = `-- name: GetPasswordByEmail :one +SELECT password +FROM users +WHERE email = $1 +` + +func (q *Queries) GetPasswordByEmail(ctx context.Context, email string) (string, error) { + row := q.db.QueryRowContext(ctx, getPasswordByEmail, email) + var password string + err := row.Scan(&password) + return password, err +} + +const getUserById = `-- name: GetUserById :one +SELECT id, index_id, email, password, fullname, created_at, updated_at +FROM users +WHERE id = $1 +` + +func (q *Queries) GetUserById(ctx context.Context, id uuid.UUID) (User, error) { + row := q.db.QueryRowContext(ctx, getUserById, id) + var i User + err := row.Scan( + &i.ID, + &i.IndexID, + &i.Email, + &i.Password, + &i.Fullname, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/db/sqlc/users_test.go b/db/sqlc/users_test.go new file mode 100644 index 0000000..20d81b6 --- /dev/null +++ b/db/sqlc/users_test.go @@ -0,0 +1,7 @@ +package db + +import "testing" + +func TestGetUserByID(t *testing.T) { + +} diff --git a/go.mod b/go.mod index 2b0fbaa..579abde 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.2 github.com/tabbed/pqtype v0.1.1 + golang.org/x/crypto v0.7.0 ) require ( @@ -42,7 +43,6 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.10 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.7.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/text v0.8.0 // indirect diff --git a/util/password.go b/util/password.go new file mode 100644 index 0000000..3b7d3f5 --- /dev/null +++ b/util/password.go @@ -0,0 +1,19 @@ +package util + +import ( + "fmt" + + "golang.org/x/crypto/bcrypt" +) + +func HashPassword(password string) (string, error) { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return "", fmt.Errorf("Failed to hash password: %w", err) + } + return string(hashedPassword), nil +} + +func CheckPassword(password string, hashedPassword string) error { + return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) +} diff --git a/util/password_test.go b/util/password_test.go new file mode 100644 index 0000000..c86c89b --- /dev/null +++ b/util/password_test.go @@ -0,0 +1,23 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/crypto/bcrypt" +) + +func TestPasswordUtil(t *testing.T) { + password := RandomString(10) + + hashedPassword, err := HashPassword(password) + require.NoError(t, err) + require.NotEmpty(t, hashedPassword) + + err = CheckPassword(password, hashedPassword) + require.NoError(t, err) + + wrongPassword := RandomString(5) + err = CheckPassword(wrongPassword, hashedPassword) + require.EqualError(t, err, bcrypt.ErrMismatchedHashAndPassword.Error()) +} diff --git a/util/random.go b/util/random.go index 49507ed..b0a36da 100644 --- a/util/random.go +++ b/util/random.go @@ -1,6 +1,7 @@ package util import ( + "fmt" "math/rand" "strings" "time" @@ -38,3 +39,7 @@ func RandomString(n int) string { return sb.String() } + +func RandomEmail() string { + return fmt.Sprintf("%s@mail.com", RandomString(5)) +}