2022-09-30 23:54:21 -04:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
2022-10-25 00:16:29 -04:00
|
|
|
"errors"
|
2022-09-30 23:54:21 -04:00
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/labstack/echo/v4"
|
|
|
|
|
|
|
|
"dynatron.me/x/blasphem/pkg/frontend"
|
2022-10-25 00:16:29 -04:00
|
|
|
"dynatron.me/x/blasphem/pkg/storage"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrInvalidAuth = errors.New("invalid auth")
|
|
|
|
ErrInvalidHandler = errors.New("no such handler")
|
2022-09-30 23:54:21 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
type Authenticator struct {
|
2022-10-25 00:16:29 -04:00
|
|
|
Flows FlowStore
|
|
|
|
Providers map[string]AuthProvider
|
2022-09-30 23:54:21 -04:00
|
|
|
}
|
|
|
|
|
2022-10-25 00:16:29 -04:00
|
|
|
func (a *Authenticator) Provider(name string) AuthProvider {
|
|
|
|
p, ok := a.Providers[name]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Authenticator) InitAuth(s storage.Store) error {
|
2022-09-30 23:54:21 -04:00
|
|
|
a.Flows = make(FlowStore)
|
2022-10-25 00:16:29 -04:00
|
|
|
hap, err := NewHAProvider(s)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX: yuck
|
|
|
|
a.Providers = map[string]AuthProvider{
|
|
|
|
hap.ProviderType(): hap,
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2022-09-30 23:54:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type AuthProvider interface {
|
|
|
|
ProviderName() string
|
|
|
|
ProviderID() *string
|
|
|
|
ProviderType() string
|
2022-10-25 00:16:29 -04:00
|
|
|
ProviderBase() AuthProviderBase
|
|
|
|
FlowSchema() []FlowSchemaItem
|
|
|
|
ValidateCreds(reqMap map[string]interface{}) bool
|
2022-09-30 23:54:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type AuthProviderBase struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
ID *string `json:"id"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
}
|
|
|
|
|
2022-10-25 00:16:29 -04:00
|
|
|
func (bp *AuthProviderBase) ProviderName() string { return bp.Name }
|
|
|
|
func (bp *AuthProviderBase) ProviderID() *string { return bp.ID }
|
|
|
|
func (bp *AuthProviderBase) ProviderType() string { return bp.Type }
|
|
|
|
func (bp *AuthProviderBase) ProviderBase() AuthProviderBase { return *bp }
|
2022-09-30 23:54:21 -04:00
|
|
|
|
|
|
|
var HomeAssistant = "homeassistant"
|
|
|
|
|
|
|
|
// TODO: make this configurable
|
2022-10-25 00:16:29 -04:00
|
|
|
func (a *Authenticator) ProvidersHandler(c echo.Context) error {
|
|
|
|
providers := []AuthProviderBase{
|
|
|
|
a.Provider(HomeAssistant).ProviderBase(),
|
2022-09-30 23:54:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return c.JSON(http.StatusOK, providers)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Authenticator) AuthorizeHandler(c echo.Context) error {
|
|
|
|
authContents, err := frontend.RootFS.Open("authorize.html")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer authContents.Close()
|
|
|
|
|
|
|
|
b, err := io.ReadAll(authContents)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.HTML(http.StatusOK, string(b))
|
|
|
|
}
|
2022-10-25 00:16:29 -04:00
|
|
|
|
|
|
|
func (a *Authenticator) Check(f *Flow, rm map[string]interface{}) error {
|
|
|
|
cID, hasCID := rm["client_id"]
|
|
|
|
if !hasCID || cID != f.request.ClientID {
|
|
|
|
return ErrInvalidAuth
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, h := range f.Handler {
|
|
|
|
if h == nil {
|
|
|
|
return ErrInvalidHandler
|
|
|
|
}
|
|
|
|
|
|
|
|
p := a.Provider(*h)
|
|
|
|
if p == nil {
|
|
|
|
return ErrInvalidAuth
|
|
|
|
}
|
|
|
|
|
|
|
|
success := p.ValidateCreds(rm)
|
|
|
|
|
|
|
|
if success {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ErrInvalidAuth
|
|
|
|
}
|