Added caching for authorization
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
4c023ad933
commit
07fa5fc986
4
go.mod
4
go.mod
|
@ -1,11 +1,12 @@
|
|||
module automation
|
||||
|
||||
go 1.17
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.3.5
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/jellydator/ttlcache/v3 v3.0.0
|
||||
github.com/joho/godotenv v1.4.0
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/kr/pretty v0.3.1
|
||||
|
@ -27,6 +28,7 @@ require (
|
|||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
|
7
go.sum
7
go.sum
|
@ -55,6 +55,8 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
|||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/jellydator/ttlcache/v3 v3.0.0 h1:zmFhqrB/4sKiEiJHhtseJsNRE32IMVmJSs4++4gaQO4=
|
||||
github.com/jellydator/ttlcache/v3 v3.0.0/go.mod h1:WwTaEmcXQ3MTjOm4bsZoDFiCu/hMvNWLO1w67RXz6h4=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||
|
@ -81,12 +83,14 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
|
|||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -104,6 +108,8 @@ golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri
|
|||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -121,6 +127,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
|||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ=
|
||||
|
|
|
@ -2,8 +2,11 @@ package google
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/jellydator/ttlcache/v3"
|
||||
)
|
||||
|
||||
type DeviceInterface interface {
|
||||
|
@ -50,38 +53,78 @@ type executeResponse struct {
|
|||
} `json:"payload"`
|
||||
}
|
||||
|
||||
func (s *Service) FullfillmentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
// @TODO We can also implement this as a cache loader function
|
||||
// Note sure how to report the correct errors in that case?
|
||||
func (s *Service) getUser(authorization string) (string, int) {
|
||||
// @TODO Make oids url configurable
|
||||
|
||||
cached := s.cache.Get(authorization)
|
||||
if cached != nil {
|
||||
return cached.Value(), http.StatusOK
|
||||
}
|
||||
|
||||
// Check if we are logged in
|
||||
{
|
||||
req, err := http.NewRequest("GET", "https://login.huizinga.dev/api/oidc/userinfo", nil)
|
||||
if err != nil {
|
||||
log.Println("Failed to make request to to login server")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
return "", http.StatusInternalServerError
|
||||
}
|
||||
|
||||
if len(r.Header["Authorization"]) > 0 {
|
||||
req.Header.Set("Authorization", r.Header["Authorization"][0])
|
||||
}
|
||||
req.Header.Set("Authorization", authorization)
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
|
||||
// If we get something other than 200, error out
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Println("Not logged in...")
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
return "", resp.StatusCode
|
||||
}
|
||||
|
||||
// Get the preferred_username from the userinfo
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println("Failed to read body")
|
||||
return "", resp.StatusCode
|
||||
}
|
||||
|
||||
var body struct {
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
}
|
||||
|
||||
err = json.Unmarshal(bodyBytes, &body)
|
||||
if err != nil {
|
||||
log.Println("Failed to marshal body")
|
||||
return "", http.StatusInternalServerError
|
||||
}
|
||||
|
||||
if len(body.PreferredUsername) == 0 {
|
||||
log.Println("Received empty username from userinfo endpoint")
|
||||
return "", http.StatusInternalServerError
|
||||
}
|
||||
|
||||
s.cache.Set(authorization, body.PreferredUsername, ttlcache.DefaultTTL)
|
||||
|
||||
return body.PreferredUsername, http.StatusOK
|
||||
}
|
||||
|
||||
func (s *Service) FullfillmentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
// Check if we are logged in
|
||||
var userID string
|
||||
if auth, ok := r.Header["Authorization"]; ok && len(auth) > 0 {
|
||||
var statusCode int
|
||||
userID, statusCode = s.getUser(auth[0])
|
||||
if statusCode != http.StatusOK {
|
||||
w.WriteHeader(statusCode)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.Println("No authorization provided")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// @TODO Make sure we receive content type json
|
||||
// @TODO Get this from userinfo, currently the scope is not set up properly to actually receive the username
|
||||
userID := "Dreaded_X"
|
||||
|
||||
fullfimentReq := &FullfillmentRequest{}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&fullfimentReq)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
|
|
@ -6,8 +6,10 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jellydator/ttlcache/v3"
|
||||
"google.golang.org/api/homegraph/v1"
|
||||
)
|
||||
|
||||
|
@ -30,13 +32,21 @@ type Provider interface {
|
|||
type Service struct {
|
||||
provider Provider
|
||||
deviceService *homegraph.DevicesService
|
||||
cache *ttlcache.Cache[string, string]
|
||||
}
|
||||
|
||||
func NewService(provider Provider, service *homegraph.Service) *Service {
|
||||
return &Service{
|
||||
s := Service{
|
||||
provider: provider,
|
||||
deviceService: homegraph.NewDevicesService(service),
|
||||
cache: ttlcache.New(
|
||||
ttlcache.WithTTL[string, string](30 * time.Minute),
|
||||
),
|
||||
}
|
||||
|
||||
go s.cache.Start()
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *Service) RequestSync(ctx context.Context, userID string) error {
|
||||
|
|
Reference in New Issue
Block a user