add login handler
This commit is contained in:
		
							
								
								
									
										1
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								main.go
									
									
									
									
									
								
							| @@ -26,6 +26,7 @@ func main() { | |||||||
| 		panic(err) | 		panic(err) | ||||||
| 	} | 	} | ||||||
| 	r := chi.NewRouter() | 	r := chi.NewRouter() | ||||||
|  | 	r.Post("/user/login", s.Login) | ||||||
| 	r.With(serve.WithPlayerID(sessions)).Get("/queue", s.Queue) | 	r.With(serve.WithPlayerID(sessions)).Get("/queue", s.Queue) | ||||||
| 	http.ListenAndServe(":8080", r) | 	http.ListenAndServe(":8080", r) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,38 +0,0 @@ | |||||||
| package serve |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"net/http" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const cookieName = "__Host-consent-v1" |  | ||||||
|  |  | ||||||
| // SetConsent registers a consent cookie on the response. |  | ||||||
| func SetConsent(w http.ResponseWriter) { |  | ||||||
| 	http.SetCookie(w, &http.Cookie{ |  | ||||||
| 		Name:     cookieName, |  | ||||||
| 		Value:    "given", |  | ||||||
| 		Expires:  time.Now().Add(20 * 365 * 24 * time.Hour), |  | ||||||
| 		Path:     "/", |  | ||||||
| 		Secure:   true, |  | ||||||
| 		HttpOnly: true, |  | ||||||
| 		SameSite: http.SameSiteLaxMode, |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NeedsConsent is a middleware that immediately responds with a 403 if the |  | ||||||
| // request does not bear a consent cookie. |  | ||||||
| func NeedsConsent(next http.Handler) http.Handler { |  | ||||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 		if _, err := r.Cookie(cookieName); err != nil { |  | ||||||
| 			http.Error(w, cookieFailed, http.StatusForbidden) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		next.ServeHTTP(w, r) |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const cookieFailed = ` |  | ||||||
| The requested resource requires consent to processing identifying information and storing necessary cookies. |  | ||||||
| I'm just a lil guy. The information is used solely for providing the service's functionality. |  | ||||||
| ` |  | ||||||
| @@ -21,6 +21,19 @@ func with[T any](ctx context.Context, v T) context.Context { | |||||||
|  |  | ||||||
| const sessionCookie = "__Host-id-v1" | const sessionCookie = "__Host-id-v1" | ||||||
|  |  | ||||||
|  | // SetSession sets a cookie carrying a session token on a response. | ||||||
|  | func SetSession(w http.ResponseWriter, s player.Session) { | ||||||
|  | 	http.SetCookie(w, &http.Cookie{ | ||||||
|  | 		Name:     sessionCookie, | ||||||
|  | 		Value:    s.String(), | ||||||
|  | 		Path:     "/", | ||||||
|  | 		Expires:  time.Now().Add(365 * 24 * time.Hour), | ||||||
|  | 		Secure:   true, | ||||||
|  | 		HttpOnly: true, | ||||||
|  | 		SameSite: http.SameSiteLaxMode, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
| // WithPlayerID is a middleware that adds a player ID to the request context | // WithPlayerID is a middleware that adds a player ID to the request context | ||||||
| // based on the session cookie content. If there is no such cookie, or its | // based on the session cookie content. If there is no such cookie, or its | ||||||
| // value is invalid, the request fails with a 403 error. | // value is invalid, the request fails with a 403 error. | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								server.go
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								server.go
									
									
									
									
									
								
							| @@ -3,6 +3,7 @@ package main | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"io" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"sync" | 	"sync" | ||||||
| @@ -20,6 +21,9 @@ import ( | |||||||
| type Server struct { | type Server struct { | ||||||
| 	l *lobby.Lobby | 	l *lobby.Lobby | ||||||
|  |  | ||||||
|  | 	creds    player.RowQuerier | ||||||
|  | 	sessions player.Execer | ||||||
|  |  | ||||||
| 	mu sync.Mutex | 	mu sync.Mutex | ||||||
| 	pp map[player.ID]*websocket.Conn | 	pp map[player.ID]*websocket.Conn | ||||||
| } | } | ||||||
| @@ -62,6 +66,40 @@ func (s *Server) left(p player.ID) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *Server) Login(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	ctx := r.Context() | ||||||
|  | 	slog := slog.With( | ||||||
|  | 		slog.String("remote", r.RemoteAddr), | ||||||
|  | 		slog.String("forwarded-for", r.Header.Get("X-Forwarded-For")), | ||||||
|  | 		slog.String("user-agent", r.UserAgent()), | ||||||
|  | 	) | ||||||
|  | 	if err := r.ParseForm(); err != nil { | ||||||
|  | 		slog.WarnContext(ctx, "error parsing form on login", "err", err.Error()) | ||||||
|  | 		http.Error(w, "what", http.StatusBadRequest) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	user, pass := r.PostFormValue("user"), r.PostFormValue("pass") | ||||||
|  | 	if user == "" || pass == "" { | ||||||
|  | 		slog.WarnContext(ctx, "missing user or pass on login") | ||||||
|  | 		http.Error(w, "missing credentials", http.StatusBadRequest) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	p, err := player.Login(ctx, s.creds, user, pass) | ||||||
|  | 	if err != nil { | ||||||
|  | 		slog.ErrorContext(ctx, "login failed", "err", err.Error()) | ||||||
|  | 		http.Error(w, "no", http.StatusUnauthorized) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	id, err := player.StartSession(ctx, s.sessions, p, time.Now()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		slog.ErrorContext(ctx, "failed to create session", "player", p, "err", err.Error()) | ||||||
|  | 		http.Error(w, "something went wrong", http.StatusInternalServerError) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	serve.SetSession(w, id) | ||||||
|  | 	io.WriteString(w, id.String()) | ||||||
|  | } | ||||||
|  |  | ||||||
| // Queue connects players to games. The connection immediately upgrades. | // Queue connects players to games. The connection immediately upgrades. | ||||||
| // This handler MUST be wrapped in [serve.WithPlayerID]. | // This handler MUST be wrapped in [serve.WithPlayerID]. | ||||||
| func (s *Server) Queue(w http.ResponseWriter, r *http.Request) { | func (s *Server) Queue(w http.ResponseWriter, r *http.Request) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user