yoake/internal/twilio/verify.go

97 lines
2.8 KiB
Go
Raw Normal View History

2022-11-07 04:45:02 -06:00
package twilio
import (
2022-11-10 19:13:09 -06:00
"crypto/subtle"
2022-11-07 04:45:02 -06:00
"fmt"
"log"
"net/http"
"net/url"
"path"
"strings"
"github.com/labstack/echo/v4"
2022-11-10 19:13:09 -06:00
"github.com/labstack/echo/v4/middleware"
2022-11-07 04:45:02 -06:00
"github.com/eternal-flame-AD/yoake/config"
2022-11-10 19:13:09 -06:00
"github.com/eternal-flame-AD/yoake/internal/auth"
2022-11-07 04:45:02 -06:00
"github.com/twilio/twilio-go/client"
)
func firstUrlValues(val url.Values) map[string]string {
res := make(map[string]string)
for k, v := range val {
res[k] = v[0]
}
return res
}
2022-11-10 19:13:09 -06:00
func VerifyMiddleware(prefix string, baseurlS string) echo.MiddlewareFunc {
baseURL, err := url.Parse(baseurlS)
if err != nil {
log.Fatalf("invalid twilio baseurl: %v", baseurlS)
}
log.Printf("twilio baseurl is %v", baseURL)
var basicAuth echo.MiddlewareFunc
if userpass := baseURL.User.String(); userpass != "" {
basicAuth = middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) {
ui := url.UserPassword(username, password)
return subtle.ConstantTimeCompare([]byte(ui.String()), []byte(userpass)) == 1, nil
})
}
2022-11-07 04:45:02 -06:00
return func(next echo.HandlerFunc) echo.HandlerFunc {
2022-11-10 19:13:09 -06:00
verifySignature := func(c echo.Context) error {
if reqAuth := auth.GetRequestAuth(c); reqAuth.Valid && reqAuth.HasRole(auth.RoleAdmin) {
return next(c)
}
2022-11-07 04:45:02 -06:00
cleanPath := path.Clean(c.Request().URL.Path)
//log.Printf("cleanPath: %s", cleanPath)
if cleanPath == prefix || strings.HasPrefix(cleanPath, prefix+"/") {
2022-11-10 19:13:09 -06:00
fullReq := c.Request().Clone(c.Request().Context())
log.Printf("original request URL: %v, scheme=%s, host=%s, user=%s", c.Request().URL, c.Request().URL.Scheme, c.Request().URL.Host, c.Request().URL.User)
fullReq.URL = baseURL.ResolveReference(c.Request().URL)
fullReq.URL.User = nil
if err := TwilioValidate(c, fullReq); err != nil {
2022-11-07 04:45:02 -06:00
c.String(http.StatusOK, "We are sorry. Request Validation Failed. This is not your fault.")
log.Printf("twilio verify failed: %v", err)
return err
}
}
return next(c)
}
2022-11-10 19:13:09 -06:00
if basicAuth != nil {
return basicAuth(verifySignature)
}
return verifySignature
2022-11-07 04:45:02 -06:00
}
}
2022-11-10 19:13:09 -06:00
func TwilioValidate(c echo.Context, req *http.Request) error {
2022-11-07 04:45:02 -06:00
conf := config.Config().Twilio
signature := req.Header.Get("X-Twilio-Signature")
if signature == "" {
if conf.SkipVerify {
return nil
}
return fmt.Errorf("no twilio signature present")
}
requestValidator := client.NewRequestValidator(conf.AuthToken)
if req.Method == "POST" {
2022-11-10 19:13:09 -06:00
form, err := c.FormParams()
if err != nil {
return err
}
if !requestValidator.Validate(req.URL.String(), firstUrlValues(form), signature) {
2022-11-07 04:45:02 -06:00
return fmt.Errorf("twilio signature verification failed")
}
} else if req.Method == "GET" {
if !requestValidator.Validate(req.URL.String(), nil, signature) {
return fmt.Errorf("twilio signature verification failed")
}
} else {
return fmt.Errorf("twilio signature verification failed: unsupported method %s", req.Method)
}
return nil
}