blasphem/pkg/auth/provider/hass/provider.go

182 lines
3.8 KiB
Go
Raw Normal View History

2022-11-12 15:56:17 -05:00
package hass
2022-10-25 00:16:29 -04:00
import (
2022-11-12 17:31:03 -05:00
"net/http"
2022-10-25 00:16:29 -04:00
"encoding/base64"
2022-10-26 19:43:51 -04:00
"github.com/rs/zerolog/log"
2022-10-25 00:16:29 -04:00
"golang.org/x/crypto/bcrypt"
2022-11-12 15:56:17 -05:00
"dynatron.me/x/blasphem/pkg/auth/provider"
2022-11-20 08:49:24 -05:00
"dynatron.me/x/blasphem/pkg/flow"
2022-10-25 00:16:29 -04:00
"dynatron.me/x/blasphem/pkg/storage"
)
const (
HAProviderKey = "auth_provider.homeassistant"
)
2022-11-11 18:02:52 -05:00
type HAUser struct {
2022-10-25 00:16:29 -04:00
Password string `json:"password"`
Username string `json:"username"`
2022-11-12 15:56:17 -05:00
provider.AuthProvider `json:"-"`
}
2022-11-13 11:55:10 -05:00
func (hau *HAUser) UserData() provider.ProviderUser {
2022-11-13 19:06:53 -05:00
return &UserData{ // strip secret
2022-12-18 21:26:34 -05:00
Username: hau.Username,
AuthProvider: hau.AuthProvider,
2022-11-12 15:56:17 -05:00
}
}
2022-12-18 21:26:34 -05:00
func (hau *HAUser) Provider() provider.AuthProvider {
return hau.AuthProvider
}
func (hau *UserData) Provider() provider.AuthProvider {
return hau.AuthProvider
}
2022-11-12 15:56:17 -05:00
type UserData struct {
Username string `json:"username"`
2022-12-18 21:26:34 -05:00
provider.AuthProvider `json:"-"`
2022-10-25 00:16:29 -04:00
}
2022-10-26 18:27:24 -04:00
2022-11-13 11:55:10 -05:00
func (ud *UserData) UserData() provider.ProviderUser {
return ud
}
2022-11-12 15:56:17 -05:00
const HomeAssistant = "homeassistant"
func (h *HAUser) ProviderUserData() interface{} { return h.UserData() }
2022-11-11 18:02:52 -05:00
2022-10-25 00:16:29 -04:00
type HomeAssistantProvider struct {
2022-11-12 15:56:17 -05:00
provider.AuthProviderBase `json:"-"`
2022-11-12 17:42:51 -05:00
Users []HAUser `json:"users"`
2022-11-20 08:49:24 -05:00
userMap map[string]*HAUser
2022-10-25 00:16:29 -04:00
}
2022-11-12 16:31:26 -05:00
func NewHAProvider(s storage.Store) (provider.AuthProvider, error) {
2022-10-25 00:16:29 -04:00
hap := &HomeAssistantProvider{
2022-11-12 15:56:17 -05:00
AuthProviderBase: provider.AuthProviderBase{
2022-10-25 00:16:29 -04:00
Name: "Home Assistant Local",
Type: HomeAssistant,
},
}
err := s.Get(HAProviderKey, hap)
if err != nil {
return hap, err
}
2022-11-13 19:06:53 -05:00
hap.userMap = make(map[string]*HAUser)
for i, u := range hap.Users {
2022-11-12 15:56:17 -05:00
hap.Users[i].AuthProvider = hap
2022-11-13 19:06:53 -05:00
hap.userMap[u.Username] = &hap.Users[i]
2022-11-12 15:56:17 -05:00
}
2022-10-25 00:16:29 -04:00
return hap, nil
}
2022-11-13 19:06:53 -05:00
func (hap *HomeAssistantProvider) Lookup(pu provider.ProviderUser) provider.ProviderUser {
u, has := hap.userMap[pu.(*HAUser).Username]
if !has {
return nil
}
return u
}
2022-10-25 00:16:29 -04:00
func (hap *HomeAssistantProvider) hashPass(p string) ([]byte, error) {
return bcrypt.GenerateFromPassword([]byte(p), bcrypt.DefaultCost)
}
2022-12-18 21:26:34 -05:00
func (hap *HomeAssistantProvider) EqualCreds(c1, c2 provider.ProviderUser) bool {
switch c1c := c1.(type) {
case *HAUser:
switch c2c := c2.(type) {
case *HAUser:
return c2c.Username == c1c.Username
case *UserData:
return c2c.Username == c1c.Username
}
case *UserData:
switch c2c := c2.(type) {
case *HAUser:
return c2c.Username == c1c.Username
case *UserData:
return c2c.Username == c1c.Username
}
}
return false
}
2022-11-12 17:31:03 -05:00
func (hap *HomeAssistantProvider) ValidateCreds(r *http.Request, rm map[string]interface{}) (provider.ProviderUser, bool) {
2022-10-25 00:16:29 -04:00
usernameE, hasU := rm["username"]
passwordE, hasP := rm["password"]
username, unStr := usernameE.(string)
password, paStr := passwordE.(string)
if !hasU || !hasP || !unStr || !paStr || username == "" || password == "" {
2022-11-11 18:02:52 -05:00
return nil, false
2022-10-25 00:16:29 -04:00
}
2022-11-11 18:02:52 -05:00
var found *HAUser
2022-10-25 00:16:29 -04:00
const dummyHash = "$2b$12$CiuFGszHx9eNHxPuQcwBWez4CwDTOcLTX5CbOpV6gef2nYuXkY7BO"
for _, v := range hap.Users { // iterate all to thwart timing attacks
if v.Username == username {
found = &v
}
}
if found == nil { // one more compare to thwart timing attacks
bcrypt.CompareHashAndPassword([]byte("foo"), []byte(dummyHash))
2022-11-11 18:02:52 -05:00
return nil, false
2022-10-25 00:16:29 -04:00
}
var hash []byte
hash, err := base64.StdEncoding.DecodeString(found.Password)
if err != nil {
2022-10-26 19:43:51 -04:00
log.Error().Err(err).Msg("b64 encode fail")
2022-11-11 18:02:52 -05:00
return nil, false
2022-10-25 00:16:29 -04:00
}
err = bcrypt.CompareHashAndPassword(hash, []byte(password))
if err == nil {
2022-12-18 21:26:34 -05:00
found.AuthProvider = hap
2022-11-11 18:02:52 -05:00
return found, true
2022-10-25 00:16:29 -04:00
}
2022-11-11 18:02:52 -05:00
return nil, false
2022-10-25 00:16:29 -04:00
}
2022-11-12 15:56:17 -05:00
func (hap *HomeAssistantProvider) NewCredData() interface{} {
2022-11-13 19:06:53 -05:00
return &HAUser{}
2022-11-12 15:56:17 -05:00
}
2022-11-20 12:51:26 -05:00
func (hap *HomeAssistantProvider) FlowSchema() flow.Schema {
return []flow.SchemaItem{
2022-10-25 00:16:29 -04:00
{
Type: "string",
Name: "username",
Required: true,
},
{
Type: "string",
Name: "password",
Required: true,
},
}
}
2022-11-12 16:31:26 -05:00
func init() {
provider.Register(HomeAssistant, NewHAProvider)
}