auth progress

This commit is contained in:
Daniel Ponte 2022-12-20 19:05:45 -05:00
parent a1005ce6bf
commit 95d72d2912
10 changed files with 110 additions and 24 deletions

View file

@ -9,7 +9,6 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"dynatron.me/x/blasphem/pkg/auth/provider" "dynatron.me/x/blasphem/pkg/auth/provider"
"dynatron.me/x/blasphem/pkg/components"
"dynatron.me/x/blasphem/pkg/storage" "dynatron.me/x/blasphem/pkg/storage"
// providers // providers
@ -35,7 +34,7 @@ type authenticator struct {
type Authenticator interface { type Authenticator interface {
ValidateAccessToken(token AccessToken) *RefreshToken ValidateAccessToken(token AccessToken) *RefreshToken
InstallRoutes(e *echo.Echo, comp components.Componenter) InstallRoutes(e *echo.Echo)
} }
type AuthError struct { type AuthError struct {
@ -43,7 +42,7 @@ type AuthError struct {
Description string `json:"error_description"` Description string `json:"error_description"`
} }
func (a *authenticator) InstallRoutes(e *echo.Echo, comp components.Componenter) { func (a *authenticator) InstallRoutes(e *echo.Echo) {
authG := e.Group("/auth") authG := e.Group("/auth")
authG.GET("/providers", a.ProvidersHandler) authG.GET("/providers", a.ProvidersHandler)
authG.POST("/token", a.TokenHandler) authG.POST("/token", a.TokenHandler)

View file

@ -8,6 +8,7 @@ import (
"dynatron.me/x/blasphem/internal/common" "dynatron.me/x/blasphem/internal/common"
"dynatron.me/x/blasphem/pkg/auth" "dynatron.me/x/blasphem/pkg/auth"
"dynatron.me/x/blasphem/pkg/blas/core"
"dynatron.me/x/blasphem/pkg/bus" "dynatron.me/x/blasphem/pkg/bus"
"dynatron.me/x/blasphem/pkg/components" "dynatron.me/x/blasphem/pkg/components"
"dynatron.me/x/blasphem/pkg/config" "dynatron.me/x/blasphem/pkg/config"
@ -21,6 +22,7 @@ type Blas struct {
storage.Store storage.Store
auth.Authenticator auth.Authenticator
Config *config.Config Config *config.Config
core.WebSocketManager
components components.ComponentStore components components.ComponentStore
} }

View file

@ -7,7 +7,7 @@ import (
"dynatron.me/x/blasphem/pkg/components" "dynatron.me/x/blasphem/pkg/components"
) )
type Setup func(core.Core) (components.Component, error) type Setup func(core.Blas) (components.Component, error)
var Registry = make(map[components.ComponentKey]Setup) var Registry = make(map[components.ComponentKey]Setup)

View file

@ -10,16 +10,35 @@ import (
"dynatron.me/x/blasphem/pkg/storage" "dynatron.me/x/blasphem/pkg/storage"
) )
type Core interface { type Blas interface {
auth.Authenticator auth.Authenticator
bus.Bus bus.Bus
storage.Store storage.Store
config.Configured config.Configured
components.Componenter
WebSocketManager
Shutdowner Shutdowner
Versioner Versioner
components.Componenter
} }
type WebSocketManager interface {
// Register registers a websocket command.
// cmd is the first part, before first slash
// dataNew is a function to create a new message datatype
RegisterWSCommand(cmd string, hnd Handler, dataNew NewData)
WSCommandHandler(cmd string) (NewData, Handler, error)
}
type WebSocketSession interface {
Go() error
Blas() Blas
}
type Handler func(wss WebSocketSession, msg interface{}) error
type NewData func() interface{}
type Shutdowner interface { type Shutdowner interface {
ShutdownBlas(context.Context) error ShutdownBlas(context.Context) error
} }

View file

@ -9,10 +9,10 @@ import (
) )
type ServeOptions struct { type ServeOptions struct {
core blas.Core core blas.Blas
} }
func Command(core blas.Core) *cobra.Command { func Command(core blas.Blas) *cobra.Command {
opts := makeOptions(core) opts := makeOptions(core)
serveCmd := &cobra.Command{ serveCmd := &cobra.Command{
Use: "serve", Use: "serve",
@ -23,7 +23,7 @@ func Command(core blas.Core) *cobra.Command {
return serveCmd return serveCmd
} }
func makeOptions(core blas.Core) *ServeOptions { func makeOptions(core blas.Blas) *ServeOptions {
return &ServeOptions{ return &ServeOptions{
core: core, core: core,
} }

View file

@ -52,7 +52,7 @@ func (fe *Frontend) AliasHandler(toFile string) echo.HandlerFunc {
func (*Frontend) Shutdown() {} func (*Frontend) Shutdown() {}
func Setup(_ core.Core) (components.Component, error) { func Setup(_ core.Blas) (components.Component, error) {
fe := &Frontend{} fe := &Frontend{}
var err error var err error

View file

@ -19,7 +19,7 @@ import (
) )
type Server struct { type Server struct {
core.Core core.Blas
*echo.Echo *echo.Echo
wg sync.WaitGroup wg sync.WaitGroup
} }
@ -32,7 +32,7 @@ func (s *Server) installRoutes() {
s.GET("/api/websocket", s.wsHandler) s.GET("/api/websocket", s.wsHandler)
s.Component(frontend.FrontendKey).(RouteHaver).InstallRoutes(s.Echo) s.Component(frontend.FrontendKey).(RouteHaver).InstallRoutes(s.Echo)
s.Core.(*blas.Blas).Authenticator.InstallRoutes(s.Echo, s.Core) s.Blas.(*blas.Blas).Authenticator.InstallRoutes(s.Echo)
for _, c := range s.Components() { for _, c := range s.Components() {
if rh, ok := c.(RouteHaver); ok { if rh, ok := c.(RouteHaver); ok {
@ -41,9 +41,9 @@ func (s *Server) installRoutes() {
} }
} }
func New(core core.Core) (s *Server, err error) { func New(core core.Blas) (s *Server, err error) {
s = &Server{ s = &Server{
Core: core, Blas: core,
Echo: echo.New(), Echo: echo.New(),
} }

View file

@ -23,5 +23,5 @@ func (s *Server) wsHandler(c echo.Context) error {
log.Debug().Str("remote", c.Request().RemoteAddr).Msg("WS") log.Debug().Str("remote", c.Request().RemoteAddr).Msg("WS")
return wsapi.New(s, c, conn).Handle() return wsapi.NewSession(s, c, conn).Go()
} }

View file

@ -1,16 +1,22 @@
package wsapi package wsapi
import ( import (
"encoding/json"
"errors"
"io" "io"
"dynatron.me/x/blasphem/pkg/auth" "dynatron.me/x/blasphem/pkg/auth"
blas "dynatron.me/x/blasphem/pkg/blas/core" "dynatron.me/x/blasphem/pkg/blas/core"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
var (
NoSuchHandlerErr = errors.New("bad websocket command")
)
type Type string type Type string
type MsgBase struct { type MsgBase struct {
Type Type `json:"type"` Type Type `json:"type"`
@ -19,7 +25,7 @@ type MsgBase struct {
type ( type (
wsSession struct { wsSession struct {
*websocket.Conn *websocket.Conn
b blas.Core b core.Blas
ec echo.Context ec echo.Context
h phaseHandler h phaseHandler
@ -27,10 +33,6 @@ type (
refreshToken *auth.RefreshToken refreshToken *auth.RefreshToken
} }
WS interface {
Handle() error
}
phaseHandler interface { phaseHandler interface {
handleMsg(io.Reader) error handleMsg(io.Reader) error
} }
@ -38,19 +40,56 @@ type (
cmdHandler struct { cmdHandler struct {
*wsSession *wsSession
} }
wsEntry struct {
dataNew core.NewData
hnd core.Handler
}
wsRegistry map[string]wsEntry
wsManager struct {
r wsRegistry
}
) )
func New(s blas.Core, c echo.Context, conn *websocket.Conn) WS { func (wsm *wsManager) RegisterWSCommand(cmd string, hnd core.Handler, dataNew core.NewData) {
wsm.r[cmd] = wsEntry{
dataNew: dataNew,
hnd: hnd,
}
}
func (wsm *wsManager) WSCommandHandler(cmd string) (core.NewData, core.Handler, error) {
wse, ok := wsm.r[cmd]
if !ok {
return nil, nil, NoSuchHandlerErr
}
return wse.dataNew, wse.hnd, nil
}
func NewManager() core.WebSocketManager {
return &wsManager{
r: make(wsRegistry),
}
}
func NewSession(s core.Blas, c echo.Context, conn *websocket.Conn) core.WebSocketSession {
ws := &wsSession{ ws := &wsSession{
Conn: conn, Conn: conn,
b: s, b: s,
ec: c, ec: c,
} }
ws.h = &authPhase{ws}
return ws return ws
} }
func (ws *wsSession) Handle() error { func (ws *wsSession) Blas() core.Blas { return ws.b }
func (ws *wsSession) Go() error {
err := ws.sendAuthRequired() err := ws.sendAuthRequired()
if err != nil { if err != nil {
return err return err
@ -76,5 +115,22 @@ type cmdMsg struct {
type MsgType string type MsgType string
func (ws *cmdHandler) handleMsg(r io.Reader) error { func (ws *cmdHandler) handleMsg(r io.Reader) error {
return nil var msgMap map[string]interface{}
err := json.NewDecoder(r).Decode(&msgMap)
if err != nil {
return err
}
msgType, ok := msgMap["type"].(string)
if !ok {
return NoSuchHandlerErr
}
newData, hand, err := ws.b.WSCommandHandler(msgType)
if !ok {
return err
}
nd := newData()
return hand(ws, nd)
} }

View file

@ -37,9 +37,18 @@ func (ap *authPhase) finishAuth(rt *auth.RefreshToken) {
ap.user = rt.User ap.user = rt.User
ap.refreshToken = rt ap.refreshToken = rt
ap.h = &cmdHandler{ap.wsSession} ap.h = &cmdHandler{ap.wsSession}
ap.sendAuthOK()
}
func (ap *authPhase) sendAuthOK() error {
return ap.WriteJSON(struct {
Type string `json:"type"`
Version string `json:"version"`
}{Type: "auth_ok", Version: ap.Blas().Version()})
} }
func (ap *authPhase) handleMsg(r io.Reader) error { func (ap *authPhase) handleMsg(r io.Reader) error {
log.Debug().Interface("ap", ap).Msg("auth handlemsg")
var authMsg authMsg var authMsg authMsg
err := json.NewDecoder(r).Decode(&authMsg) err := json.NewDecoder(r).Decode(&authMsg)
if err != nil { if err != nil {
@ -49,6 +58,7 @@ func (ap *authPhase) handleMsg(r io.Reader) error {
refreshToken := ap.b.ValidateAccessToken(authMsg.AccessToken) refreshToken := ap.b.ValidateAccessToken(authMsg.AccessToken)
if refreshToken != nil { if refreshToken != nil {
ap.finishAuth(refreshToken) ap.finishAuth(refreshToken)
return ap.sendAuthOK()
} }
log.Error().Str("remote", ap.ec.Request().RemoteAddr).Msg("websocket auth failed") log.Error().Str("remote", ap.ec.Request().RemoteAddr).Msg("websocket auth failed")