package auth import ( "encoding/base64" "golang.org/x/crypto/bcrypt" "dynatron.me/x/blasphem/pkg/storage" ) const ( HAProviderKey = "auth_provider.homeassistant" ) type User struct { Password string `json:"password"` Username string `json:"username"` } type HomeAssistantProvider struct { AuthProviderBase `json:"-"` Users []User `json:"users"` } func NewHAProvider(s storage.Store) (*HomeAssistantProvider, error) { hap := &HomeAssistantProvider{ AuthProviderBase: AuthProviderBase{ Name: "Home Assistant Local", Type: HomeAssistant, }, } err := s.Get(HAProviderKey, hap) if err != nil { return hap, err } return hap, nil } func (hap *HomeAssistantProvider) hashPass(p string) ([]byte, error) { return bcrypt.GenerateFromPassword([]byte(p), bcrypt.DefaultCost) } func (hap *HomeAssistantProvider) ValidateCreds(rm map[string]interface{}) bool { usernameE, hasU := rm["username"] passwordE, hasP := rm["password"] username, unStr := usernameE.(string) password, paStr := passwordE.(string) if !hasU || !hasP || !unStr || !paStr || username == "" || password == "" { return false } var found *User 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)) return false } var hash []byte hash, err := base64.StdEncoding.DecodeString(found.Password) if err != nil { // XXX: probably log this return false } err = bcrypt.CompareHashAndPassword(hash, []byte(password)) if err == nil { return true } return false } func (hap *HomeAssistantProvider) FlowSchema() []FlowSchemaItem { return []FlowSchemaItem{ { Type: "string", Name: "username", Required: true, }, { Type: "string", Name: "password", Required: true, }, } }