diff --git a/pkg/userstore/error.go b/pkg/userstore/error.go index 3dcf321..4f2ec20 100644 --- a/pkg/userstore/error.go +++ b/pkg/userstore/error.go @@ -6,4 +6,5 @@ var ( ErrCantOpenDatabase = errors.New("failed to open database") ErrCantExecuteQuery = errors.New("cant execute query") ErrCantEncryptPassword = errors.New("cant encrypt password") + ErrPasswordDontMatch = errors.New("user password dont match") ) diff --git a/pkg/userstore/users.go b/pkg/userstore/users.go index ffac164..b03e67c 100644 --- a/pkg/userstore/users.go +++ b/pkg/userstore/users.go @@ -1,10 +1,20 @@ package userstore import ( + "database/sql" + "fmt" + "time" + "git.keks.cloud/kekskurse/miniauth/pkg/utils" "golang.org/x/crypto/bcrypt" ) +type User struct { + Username string + CreatedAt time.Time + UpdatedAt time.Time +} + func (s Store) UserWrite(username, password string) (int64, error) { query := "INSERT INTO users (username, password) VALUES (?, ?);" log := s.log.With().Str("func", "UserWrite").Str("query", query).Str("username", username).Logger() @@ -26,6 +36,39 @@ func (s Store) UserWrite(username, password string) (int64, error) { return id, nil } +func (s Store) UserCheckPassword(username, password string) (User, error) { + query := fmt.Sprintf("SELECT %v FROM users WHERE username = ?", s.userGetFields()) + log := s.log.With().Str("func", "UserLogin").Str("username", username).Str("query", query).Logger() + + var pwHash string + row := s.db.QueryRow(query, username) + user, pwHash, err := s.userScan(row) + if err != nil { + return User{}, utils.WrapError(ErrCantExecuteQuery, err, log) + } + + err = bcrypt.CompareHashAndPassword([]byte(pwHash), []byte(password)) + if err != nil { + return User{}, utils.WrapError(ErrPasswordDontMatch, err, log) + } + + return user, nil +} + +func (s Store) userGetFields() string { + return "username, password, created_at, updated_at" +} + +func (s Store) userScan(row *sql.Row) (User, string, error) { + u := User{} + var pwHash string + err := row.Scan(&u.Username, &pwHash, &u.CreatedAt, &u.UpdatedAt) + if err != nil { + return User{}, "", err + } + return u, pwHash, nil +} + func (s Store) UserExists(username string) (bool, error) { query := "SELECT COUNT(*) FROM users WHERE username = ?" log := s.log.With().Str("func", "UserExists").Str("query", query).Str("username", username).Logger() diff --git a/pkg/userstore/users_test.go b/pkg/userstore/users_test.go index b445c63..ccf4928 100644 --- a/pkg/userstore/users_test.go +++ b/pkg/userstore/users_test.go @@ -2,6 +2,7 @@ package userstore import ( "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -134,3 +135,55 @@ func TestUserRemoveFaildMailExists(t *testing.T) { assert.Nil(t, err, "should be abel to get user count without error") assert.Equal(t, 2, count, "should found 2 users") } + +func TestUserPasswordCheck(t *testing.T) { + store, err := NewDummyStore() + assert.Nil(t, err, "[setup] should get dummy store without error") + + _, err = store.UserWrite("kekskurse", "kekskurse") + assert.Nil(t, err, "[setup] should be abel to create user") + + tts := []struct { + name string + username string + password string + expectErr error + expectErrString string + expectUsername string + }{ + { + name: "user not found", + expectErr: ErrCantExecuteQuery, + expectErrString: "cant execute query: sql: no rows in result set", + }, + { + name: "wrong password", + username: "kekskurse", + expectErr: ErrPasswordDontMatch, + expectErrString: "user password dont match: crypto/bcrypt: hashedPassword is not the hash of the given password", + }, + { + name: "successfull", + username: "kekskurse", + password: "kekskurse", + expectErr: nil, + expectUsername: "kekskurse", + }, + } + + for _, tt := range tts { + t.Run(tt.name, func(t *testing.T) { + user, err := store.UserCheckPassword(tt.username, tt.password) + if tt.expectErr != nil { + assert.ErrorIs(t, err, tt.expectErr) + assert.Equal(t, tt.expectErrString, err.Error()) + return + } + + assert.Nil(t, err) + assert.Equal(t, tt.expectUsername, user.Username) + assert.Equal(t, time.Now().Year(), user.CreatedAt.Year()) + assert.Equal(t, time.Now().Year(), user.UpdatedAt.Year()) + }) + } +}