auth progress
This commit is contained in:
parent
a1005ce6bf
commit
95d72d2912
10 changed files with 110 additions and 24 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in a new issue