Config UI: add redirect uri suggestion service (#25478)
This commit is contained in:
parent
694816512e
commit
8d94166d93
9 changed files with 101 additions and 121 deletions
|
|
@ -1,33 +0,0 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/evcc-io/evcc/api/globalconfig"
|
||||
)
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
registry []func()
|
||||
)
|
||||
|
||||
var config globalconfig.Network
|
||||
|
||||
func Config() globalconfig.Network {
|
||||
return config
|
||||
}
|
||||
|
||||
func Register(fun func()) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
registry = append(registry, fun)
|
||||
}
|
||||
|
||||
func Start(conf globalconfig.Network) {
|
||||
config = conf
|
||||
|
||||
for _, fun := range registry {
|
||||
go fun()
|
||||
}
|
||||
}
|
||||
34
server/network/service.go
Normal file
34
server/network/service.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/evcc-io/evcc/api/globalconfig"
|
||||
"github.com/evcc-io/evcc/server/service"
|
||||
)
|
||||
|
||||
var config globalconfig.Network
|
||||
|
||||
const CallbackPath = "/providerauth/callback"
|
||||
|
||||
func init() {
|
||||
// auth service is registered here to avoid import cycle
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /redirecturi", getRedirectUri)
|
||||
|
||||
service.Register("auth", mux)
|
||||
}
|
||||
|
||||
func Start(conf globalconfig.Network) {
|
||||
config = conf
|
||||
}
|
||||
|
||||
func Config() globalconfig.Network {
|
||||
return config
|
||||
}
|
||||
|
||||
func getRedirectUri(w http.ResponseWriter, req *http.Request) {
|
||||
uri := config.ExternalURL() + CallbackPath
|
||||
json.NewEncoder(w).Encode([]string{uri})
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ params:
|
|||
help:
|
||||
en: "Redirect URI of the evcc instance. Must match the redirect URI set in the Viessmann developer portal."
|
||||
de: "Redirect-URI der evcc-Instanz. Muss mit der Redirect URI übereinstimmen, die Viessmann Developer Portal konfiguriert ist."
|
||||
service: auth/redirecturi
|
||||
example: "https://evcc.example.org/providerauth/callback"
|
||||
- name: gateway_serial
|
||||
required: true
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ params:
|
|||
help:
|
||||
en: "Redirect URI of your evcc instance. Must match the redirect URI set in your Volvo Developer App."
|
||||
de: "Redirect-URI deiner evcc-Instanz. Muss mit der Redirect-URI übereinstimmen, die in deiner Volvo Developer App festgelegt ist."
|
||||
service: auth/redirecturi
|
||||
example: "https://evcc.example.org/providerauth/callback"
|
||||
- name: vin
|
||||
example: WF0FXX...
|
||||
|
|
|
|||
|
|
@ -1,23 +1,50 @@
|
|||
package homeassistant
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type instance struct {
|
||||
URI string
|
||||
type proxyInstance struct {
|
||||
mu sync.Mutex
|
||||
home, uri string
|
||||
oauth2.TokenSource
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
instances = make(map[string]*instance)
|
||||
)
|
||||
func (inst *proxyInstance) URI() string {
|
||||
inst.mu.Lock()
|
||||
defer inst.mu.Unlock()
|
||||
|
||||
func instanceByName(name string) *instance {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return instances[name]
|
||||
if inst.uri == "" {
|
||||
inst.uri = instanceUriByName(inst.home)
|
||||
|
||||
if inst.uri == "" {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return inst.uri
|
||||
}
|
||||
|
||||
func (inst *proxyInstance) Token() (*oauth2.Token, error) {
|
||||
uri := inst.URI()
|
||||
if uri == "" {
|
||||
return nil, fmt.Errorf("unknown instance: %s", inst.home)
|
||||
}
|
||||
|
||||
inst.mu.Lock()
|
||||
defer inst.mu.Unlock()
|
||||
|
||||
if inst.TokenSource == nil {
|
||||
ts, err := NewHomeAssistant(inst.home, uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inst.TokenSource = ts
|
||||
}
|
||||
|
||||
return inst.TokenSource.Token()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,17 +26,17 @@ func NewHomeAssistantFromConfig(other map[string]any) (oauth2.TokenSource, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
inst := instanceByName(cc.Home)
|
||||
if inst == nil {
|
||||
uri := instanceUriByName(cc.Home)
|
||||
if uri == "" {
|
||||
return nil, fmt.Errorf("unknown instance: %s", cc.Home)
|
||||
}
|
||||
|
||||
return NewHomeAssistant(cc.Home, inst.URI)
|
||||
return NewHomeAssistant(cc.Home, uri)
|
||||
}
|
||||
|
||||
func NewHomeAssistant(name, uri string) (oauth2.TokenSource, error) {
|
||||
func NewHomeAssistant(home, uri string) (oauth2.TokenSource, error) {
|
||||
extUrl := network.Config().ExternalURL()
|
||||
redirectUri := extUrl + "/providerauth/callback"
|
||||
redirectUri := extUrl + network.CallbackPath
|
||||
|
||||
log := util.NewLogger("homeassistant")
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, request.NewClient(log))
|
||||
|
|
@ -50,5 +50,5 @@ func NewHomeAssistant(name, uri string) (oauth2.TokenSource, error) {
|
|||
},
|
||||
}
|
||||
|
||||
return auth.NewOAuth(ctx, "HomeAssistant", name, &oc)
|
||||
return auth.NewOAuth(ctx, "HomeAssistant", home, &oc)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
package homeassistant
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type proxyInstance struct {
|
||||
mu sync.Mutex
|
||||
home string
|
||||
instance *instance
|
||||
}
|
||||
|
||||
func (inst *proxyInstance) URI() string {
|
||||
inst.mu.Lock()
|
||||
defer inst.mu.Unlock()
|
||||
|
||||
if inst.instance == nil {
|
||||
inst.instance = instanceByName(inst.home)
|
||||
|
||||
if inst.instance == nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return inst.instance.URI
|
||||
}
|
||||
|
||||
func (inst *proxyInstance) Token() (*oauth2.Token, error) {
|
||||
inst.mu.Lock()
|
||||
defer inst.mu.Unlock()
|
||||
|
||||
if inst.instance == nil {
|
||||
inst.instance = instanceByName(inst.home)
|
||||
|
||||
if inst.instance == nil {
|
||||
return nil, fmt.Errorf("unknown instance: %s", inst.home)
|
||||
}
|
||||
}
|
||||
|
||||
return inst.instance.Token()
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ func getHomes(w http.ResponseWriter, req *http.Request) {
|
|||
func getEntities(w http.ResponseWriter, req *http.Request) {
|
||||
home := req.PathValue("home")
|
||||
|
||||
if instanceByName(home) == nil {
|
||||
if instanceUriByName(home) == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,36 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/evcc-io/evcc/server/network"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/libp2p/zeroconf/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
instances = make(map[string]string)
|
||||
)
|
||||
|
||||
func init() {
|
||||
network.Register(scan)
|
||||
go scan()
|
||||
}
|
||||
|
||||
func instanceUriByName(name string) string {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return instances[name]
|
||||
}
|
||||
|
||||
func addInstance(name, uri string) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
instances[name] = uri
|
||||
}
|
||||
|
||||
func scan() {
|
||||
ctx := context.Background()
|
||||
|
||||
log := util.NewLogger("homeassistant")
|
||||
entries := make(chan *zeroconf.ServiceEntry, 1)
|
||||
|
||||
go func() {
|
||||
|
|
@ -36,9 +52,7 @@ func scan() {
|
|||
}
|
||||
}
|
||||
|
||||
if err := authorize(se.Instance, uri); err != nil {
|
||||
log.ERROR.Println(err)
|
||||
}
|
||||
addInstance(se.Instance, uri)
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
|
@ -47,27 +61,7 @@ func scan() {
|
|||
}()
|
||||
|
||||
if err := zeroconf.Browse(ctx, "_home-assistant._tcp.", "local.", entries); err != nil {
|
||||
log := util.NewLogger("homeassistant")
|
||||
log.ERROR.Println("zeroconf: failed to browse:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func authorize(name, uri string) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if _, ok := instances[name]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
ts, err := NewHomeAssistant(name, uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
instances[name] = &instance{
|
||||
URI: uri,
|
||||
TokenSource: ts,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue