165 lines
3.7 KiB
Go
165 lines
3.7 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"github.com/influxdata/platform"
|
|
"github.com/julienschmidt/httprouter"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// SessionBackend is all services and associated parameters required to construct
|
|
// the SessionHandler.
|
|
type SessionBackend struct {
|
|
Logger *zap.Logger
|
|
|
|
BasicAuthService platform.BasicAuthService
|
|
SessionService platform.SessionService
|
|
}
|
|
|
|
func NewSessionBackend(b *APIBackend) *SessionBackend {
|
|
return &SessionBackend{
|
|
Logger: b.Logger.With(zap.String("handler", "session")),
|
|
|
|
BasicAuthService: b.BasicAuthService,
|
|
SessionService: b.SessionService,
|
|
}
|
|
}
|
|
|
|
// SessionHandler represents an HTTP API handler for authorizations.
|
|
type SessionHandler struct {
|
|
*httprouter.Router
|
|
Logger *zap.Logger
|
|
|
|
BasicAuthService platform.BasicAuthService
|
|
SessionService platform.SessionService
|
|
}
|
|
|
|
// NewSessionHandler returns a new instance of SessionHandler.
|
|
func NewSessionHandler(b *SessionBackend) *SessionHandler {
|
|
h := &SessionHandler{
|
|
Router: NewRouter(),
|
|
Logger: b.Logger,
|
|
|
|
BasicAuthService: b.BasicAuthService,
|
|
SessionService: b.SessionService,
|
|
}
|
|
|
|
h.HandlerFunc("POST", "/api/v2/signin", h.handleSignin)
|
|
h.HandlerFunc("POST", "/api/v2/signout", h.handleSignout)
|
|
return h
|
|
}
|
|
|
|
// handleSignin is the HTTP handler for the POST /signin route.
|
|
func (h *SessionHandler) handleSignin(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
|
|
req, err := decodeSigninRequest(ctx, r)
|
|
if err != nil {
|
|
h.Logger.Info("failed to decode request", zap.Error(err))
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
if err := h.BasicAuthService.ComparePassword(ctx, req.Username, req.Password); err != nil {
|
|
// Don't log here, it should already be handled by the service
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
s, e := h.SessionService.CreateSession(ctx, req.Username)
|
|
if e != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
encodeCookieSession(w, s)
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
type signinRequest struct {
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func decodeSigninRequest(ctx context.Context, r *http.Request) (*signinRequest, *platform.Error) {
|
|
u, p, ok := r.BasicAuth()
|
|
if !ok {
|
|
return nil, &platform.Error{
|
|
Code: platform.EInvalid,
|
|
Msg: "invalid basic auth",
|
|
}
|
|
}
|
|
|
|
return &signinRequest{
|
|
Username: u,
|
|
Password: p,
|
|
}, nil
|
|
}
|
|
|
|
// handleSignout is the HTTP handler for the POST /signout route.
|
|
func (h *SessionHandler) handleSignout(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
|
|
req, err := decodeSignoutRequest(ctx, r)
|
|
if err != nil {
|
|
h.Logger.Info("failed to decode request", zap.Error(err))
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
if err := h.SessionService.ExpireSession(ctx, req.Key); err != nil {
|
|
EncodeError(ctx, err, w)
|
|
return
|
|
}
|
|
|
|
// TODO(desa): not sure what to do here maybe redirect?
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
type signoutRequest struct {
|
|
Key string
|
|
}
|
|
|
|
func decodeSignoutRequest(ctx context.Context, r *http.Request) (*signoutRequest, *platform.Error) {
|
|
key, err := decodeCookieSession(ctx, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &signoutRequest{
|
|
Key: key,
|
|
}, nil
|
|
}
|
|
|
|
const cookieSessionName = "session"
|
|
|
|
func encodeCookieSession(w http.ResponseWriter, s *platform.Session) {
|
|
c := &http.Cookie{
|
|
Name: cookieSessionName,
|
|
Value: s.Key,
|
|
}
|
|
|
|
http.SetCookie(w, c)
|
|
}
|
|
func decodeCookieSession(ctx context.Context, r *http.Request) (string, *platform.Error) {
|
|
c, err := r.Cookie(cookieSessionName)
|
|
if err != nil {
|
|
return "", &platform.Error{
|
|
Err: err,
|
|
Code: platform.EInvalid,
|
|
}
|
|
}
|
|
return c.Value, nil
|
|
}
|
|
|
|
// SetCookieSession adds a cookie for the session to an http request
|
|
func SetCookieSession(key string, r *http.Request) {
|
|
c := &http.Cookie{
|
|
Name: cookieSessionName,
|
|
Value: key,
|
|
}
|
|
|
|
r.AddCookie(c)
|
|
}
|