summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorMichael Hunteman <michael@huntm.net>2024-06-23 13:55:42 -0700
committerMichael Hunteman <michael@huntm.net>2024-06-23 13:55:42 -0700
commit07752babb4e692452e1cd7f2133c4d8dde1b3b1c (patch)
treeb3be7698f1af43f83bccd3bbbf6e19cd03532f1b /server
parent4bf5d1a620dfe96ea9593d44cfcd0f142fcdec61 (diff)
Authenticate UI users
Diffstat (limited to 'server')
-rw-r--r--server/cmd/main.go83
-rw-r--r--server/go.mod10
-rw-r--r--server/go.sum14
-rw-r--r--server/guests/models.go17
-rw-r--r--server/guests/store.go31
5 files changed, 142 insertions, 13 deletions
diff --git a/server/cmd/main.go b/server/cmd/main.go
index f886e2b..5b81b66 100644
--- a/server/cmd/main.go
+++ b/server/cmd/main.go
@@ -2,6 +2,8 @@ package main
import (
"context"
+ "crypto/rand"
+ "encoding/base64"
"encoding/json"
"fmt"
"log"
@@ -9,6 +11,7 @@ import (
"os"
"regexp"
+ "github.com/golang-jwt/jwt/v5"
"github.com/jackc/pgx/v5/pgxpool"
"git.huntm.net/wedding/server/guests"
@@ -19,6 +22,7 @@ type guestHandler struct {
}
type guestStore interface {
+ FindGuest(creds guests.Credentials) (guests.Guest, error)
Get() ([]guests.Guest, error)
Add(guest guests.Guest) error
Update(guest guests.Guest) error
@@ -41,6 +45,60 @@ func newGuestHandler(s guestStore) *guestHandler {
}
}
+func (h *guestHandler) login(w http.ResponseWriter, r *http.Request) {
+ var creds guests.Credentials
+ err := json.NewDecoder(r.Body).Decode(&creds)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ defer r.Body.Close()
+
+ guest, err := h.store.FindGuest(creds)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusUnauthorized)
+ return
+ }
+
+ claims := &guests.Claims{
+ Guest: guest,
+ RegisteredClaims: jwt.RegisteredClaims{},
+ }
+
+ key := make([]byte, 32)
+ _, err = rand.Read(key)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ secretKey := []byte(base64.StdEncoding.EncodeToString(key))
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+ tokenString, err := token.SignedString(secretKey)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ loginResponse := &guests.LoginResponse{
+ Guest: guest,
+ Token: tokenString,
+ }
+
+ jsonBytes, err := json.Marshal(loginResponse)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+ http.SetCookie(w, &http.Cookie{
+ Name: "token",
+ Value: tokenString,
+ })
+ w.Write(jsonBytes)
+}
+
func (h *guestHandler) getGuests(w http.ResponseWriter, _ *http.Request) {
guests, err := h.store.Get()
if err != nil {
@@ -106,13 +164,17 @@ func (h *guestHandler) updateGuest(w http.ResponseWriter, r *http.Request) {
err = h.store.Update(guest)
if err != nil {
- http.Error(w, "Guest not found", http.StatusBadRequest)
+ http.Error(w, "Cannot update guest", http.StatusBadRequest)
return
}
}
func (h *guestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch {
+ case r.Method == http.MethodOptions:
+ w.WriteHeader(http.StatusOK)
+ case r.Method == http.MethodPost && r.URL.Path == "/guests/login":
+ h.login(w, r)
case r.Method == http.MethodGet && guestRe.MatchString(r.URL.Path):
h.getGuests(w, r)
case r.Method == http.MethodPost && guestRe.MatchString(r.URL.Path):
@@ -124,6 +186,23 @@ func (h *guestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
+func enableCors(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if isPreflight(r) {
+ w.Header().Add("Access-Control-Allow-Methods", "*")
+ }
+ w.Header().Add("Access-Control-Allow-Origin", "*")
+ w.Header().Add("Access-Control-Allow-Headers", "*")
+ next.ServeHTTP(w, r)
+ })
+}
+
+func isPreflight(r *http.Request) bool {
+ return r.Method == "OPTIONS" &&
+ r.Header.Get("Origin") != "" &&
+ r.Header.Get("Access-Control-Request-Method") != ""
+}
+
func main() {
db, err := pgxpool.New(context.Background(), fmt.Sprintf("postgres://%s:%s@%s:%s/%s", user, pass, host, port, database))
if err != nil {
@@ -136,5 +215,5 @@ func main() {
mux := http.NewServeMux()
mux.Handle("/guests/", guestHandler)
- log.Fatal(http.ListenAndServe(":8080", mux))
+ log.Fatal(http.ListenAndServe(":8080", enableCors(mux)))
}
diff --git a/server/go.mod b/server/go.mod
index 3ab08ef..49af49e 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -2,13 +2,17 @@ module git.huntm.net/wedding/server
go 1.22.2
-require github.com/jackc/pgx/v5 v5.5.5
+require (
+ github.com/golang-jwt/jwt/v5 v5.2.1
+ github.com/jackc/pgx/v5 v5.5.5
+)
require (
github.com/jackc/pgpassfile v1.0.0 // indirect
- github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
+ github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
+ github.com/stretchr/testify v1.8.4 // indirect
golang.org/x/crypto v0.23.0 // indirect
- golang.org/x/sync v0.1.0 // indirect
+ golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.15.0 // indirect
)
diff --git a/server/go.sum b/server/go.sum
index 23b6653..b93b74b 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -1,10 +1,12 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
-github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
-github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
+github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
@@ -14,12 +16,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/server/guests/models.go b/server/guests/models.go
index c68a4c3..5915f81 100644
--- a/server/guests/models.go
+++ b/server/guests/models.go
@@ -1,5 +1,7 @@
package guests
+import "github.com/golang-jwt/jwt/v5"
+
type Guest struct {
Id int `json:"id"`
FirstName string `json:"firstName"`
@@ -15,3 +17,18 @@ type PartyGuest struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
}
+
+type Credentials struct {
+ FirstName string `json:"firstName"`
+ LastName string `json:"lastName"`
+}
+
+type Claims struct {
+ Guest Guest `json:"guest"`
+ jwt.RegisteredClaims
+}
+
+type LoginResponse struct {
+ Guest Guest `json:"guest"`
+ Token string `json:"token"`
+}
diff --git a/server/guests/store.go b/server/guests/store.go
index 597cc80..bedc646 100644
--- a/server/guests/store.go
+++ b/server/guests/store.go
@@ -2,6 +2,7 @@ package guests
import (
"context"
+ "errors"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -16,6 +17,26 @@ func NewMemStore(db *pgxpool.Pool) *MemStore {
}
}
+func (m MemStore) FindGuest(creds Credentials) (Guest, error) {
+ rows, err := m.db.Query(context.Background(), "select * from guest")
+ var guest Guest
+ if err != nil {
+ return guest, err
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ err := rows.Scan(&guest.Id, &guest.FirstName, &guest.LastName, &guest.Attendance, &guest.Email, &guest.Message, &guest.PartySize)
+ if err != nil {
+ return guest, err
+ }
+ if guest.FirstName == creds.FirstName && guest.LastName == creds.LastName {
+ return guest, nil
+ }
+ }
+ return guest, errors.New("Guest does not exist")
+}
+
func (m MemStore) Get() ([]Guest, error) {
rows, err := m.db.Query(context.Background(), "select * from guest")
if err != nil {
@@ -79,9 +100,15 @@ func (m MemStore) Update(guest Guest) error {
return err
}
- statement = "update party set first_name = $1, last_name = $2 where guest_id = $3"
+ statement = "delete from party where guest_id = $1"
+ _, err = m.db.Exec(context.Background(), statement, guest.Id)
+ if err != nil {
+ return err
+ }
+
+ statement = "insert into party (guest_id, first_name, last_name) values ($1, $2, $3)"
for _, pg := range guest.PartyList {
- _, err = m.db.Exec(context.Background(), statement, pg.FirstName, pg.LastName, guest.Id)
+ _, err = m.db.Exec(context.Background(), statement, guest.Id, pg.FirstName, pg.LastName)
if err != nil {
return err
}