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"
"dynatron.me/x/blasphem/pkg/auth/provider"
"dynatron.me/x/blasphem/pkg/components"
"dynatron.me/x/blasphem/pkg/storage"
// providers
@ -35,7 +34,7 @@ type authenticator struct {
type Authenticator interface {
ValidateAccessToken(token AccessToken) *RefreshToken
InstallRoutes(e *echo.Echo, comp components.Componenter)
InstallRoutes(e *echo.Echo)
}
type AuthError struct {
@ -43,7 +42,7 @@ type AuthError struct {
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.GET("/providers", a.ProvidersHandler)
authG.POST("/token", a.TokenHandler)

View file

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

View file

@ -7,7 +7,7 @@ import (
"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)

View file

@ -10,16 +10,35 @@ import (
"dynatron.me/x/blasphem/pkg/storage"
)
type Core interface {
type Blas interface {
auth.Authenticator
bus.Bus
storage.Store
config.Configured
components.Componenter
WebSocketManager
Shutdowner
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 {
ShutdownBlas(context.Context) error
}

View file

@ -9,10 +9,10 @@ import (
)
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)
serveCmd := &cobra.Command{
Use: "serve",
@ -23,7 +23,7 @@ func Command(core blas.Core) *cobra.Command {
return serveCmd
}
func makeOptions(core blas.Core) *ServeOptions {
func makeOptions(core blas.Blas) *ServeOptions {
return &ServeOptions{
core: core,
}

View file

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

View file

@ -19,7 +19,7 @@ import (
)
type Server struct {
core.Core
core.Blas
*echo.Echo
wg sync.WaitGroup
}
@ -32,7 +32,7 @@ func (s *Server) installRoutes() {
s.GET("/api/websocket", s.wsHandler)
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() {
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{
Core: core,
Blas: core,
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")
return wsapi.New(s, c, conn).Handle()
return wsapi.NewSession(s, c, conn).Go()
}

View file

@ -1,16 +1,22 @@
package wsapi
import (
"encoding/json"
"errors"
"io"
"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/labstack/echo/v4"
"github.com/rs/zerolog/log"
)
var (
NoSuchHandlerErr = errors.New("bad websocket command")
)
type Type string
type MsgBase struct {
Type Type `json:"type"`
@ -19,7 +25,7 @@ type MsgBase struct {
type (
wsSession struct {
*websocket.Conn
b blas.Core
b core.Blas
ec echo.Context
h phaseHandler
@ -27,10 +33,6 @@ type (
refreshToken *auth.RefreshToken
}
WS interface {
Handle() error
}
phaseHandler interface {
handleMsg(io.Reader) error
}
@ -38,19 +40,56 @@ type (
cmdHandler struct {
*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{
Conn: conn,
b: s,
ec: c,
}
ws.h = &authPhase{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()
if err != nil {
return err
@ -76,5 +115,22 @@ type cmdMsg struct {
type MsgType string
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.refreshToken = rt
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 {
log.Debug().Interface("ap", ap).Msg("auth handlemsg")
var authMsg authMsg
err := json.NewDecoder(r).Decode(&authMsg)
if err != nil {
@ -49,6 +58,7 @@ func (ap *authPhase) handleMsg(r io.Reader) error {
refreshToken := ap.b.ValidateAccessToken(authMsg.AccessToken)
if refreshToken != nil {
ap.finishAuth(refreshToken)
return ap.sendAuthOK()
}
log.Error().Str("remote", ap.ec.Request().RemoteAddr).Msg("websocket auth failed")