add auth middleware

This commit is contained in:
gohumble 2022-08-19 23:35:27 +02:00
parent a669ea86ac
commit 4bcbd2fefe
3 changed files with 92 additions and 6 deletions

View file

@ -1,8 +1,15 @@
package api
import (
"context"
"encoding/base64"
"fmt"
"github.com/LightningTipBot/LightningTipBot/internal/lnbits"
"github.com/LightningTipBot/LightningTipBot/internal/telegram"
"gorm.io/gorm"
"net/http"
"net/http/httputil"
"strings"
log "github.com/sirupsen/logrus"
)
@ -16,6 +23,74 @@ func LoggingMiddleware(prefix string, next http.HandlerFunc) http.HandlerFunc {
}
}
type AuthType string
const (
AuthTypeBasic AuthType = "Basic"
AuthTypeBearer AuthType = "Bearer"
)
func AuthorizationMiddleware(database *gorm.DB, authType AuthType, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization")
// check if the user is banned
if auth != "" {
_, password, ok := parseAuth(authType, auth)
if !ok {
return
}
// first we make sure that the password is not already "banned_"
if strings.Contains(password, "_") || strings.HasPrefix(password, "banned_") {
log.Warnf("[AuthorizationMiddleware] Banned user %s. Not forwarding request", password)
return
}
// then we check whether the "normal" password provided is in the database (it should be not if the user is banned)
user := &lnbits.User{}
tx := database.Where("wallet_adminkey = ? COLLATE NOCASE", password).First(user)
if tx.Error != nil {
tx = database.Where("wallet_inkey = ? COLLATE NOCASE", password).First(user)
log.Warnf("[AuthorizationMiddleware] Could not get wallet admin key %s: %v", password, tx.Error)
if tx.Error != nil {
log.Warnf("[AuthorizationMiddleware] need admin key to pay invoice %s: %v", password, tx.Error)
return
}
if r.URL.Path == "/api/v1/payinvoice" {
log.Warnf("[AuthorizationMiddleware] need admin key to pay invoice %s: %v", password, tx.Error)
return
}
}
r.Context()
log.Debugf("[AuthorizationMiddleware] User: %s", telegram.GetUserStr(user.Telegram))
r = r.WithContext(context.WithValue(r.Context(), "user", user))
}
next.ServeHTTP(w, r)
}
}
// parseAuth parses an HTTP Basic Authentication string.
// "Bearer QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
func parseAuth(authType AuthType, auth string) (username, password string, ok bool) {
parse := func(prefix string) (username, password string, ok bool) {
// Case insensitive prefix match. See Issue 22736.
if len(auth) < len(prefix) || !strings.EqualFold(auth[:len(prefix)], prefix) {
return
}
c, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
if err != nil {
return
}
cs := string(c)
s := strings.IndexByte(cs, ':')
if s < 0 {
return
}
return cs[:s], cs[s+1:], true
}
return parse(fmt.Sprintf("%s ", authType))
}
func dump(r *http.Request) string {
x, err := httputil.DumpRequest(r, true)
if err != nil {

View file

@ -2,6 +2,7 @@ package api
import (
"encoding/json"
"gorm.io/gorm"
"net/http"
"time"
@ -42,6 +43,12 @@ func (w *Server) ListenAndServe() {
func (w *Server) PathPrefix(path string, handler http.Handler) {
w.router.PathPrefix(path).Handler(handler)
}
func (w *Server) AppendAuthorizedRoute(path string, authType AuthType, database *gorm.DB, handler func(http.ResponseWriter, *http.Request), methods ...string) {
r := w.router.HandleFunc(path, LoggingMiddleware("API", AuthorizationMiddleware(database, authType, handler)))
if len(methods) > 0 {
r.Methods(methods...)
}
}
func (w *Server) AppendRoute(path string, handler func(http.ResponseWriter, *http.Request), methods ...string) {
r := w.router.HandleFunc(path, LoggingMiddleware("API", handler))
if len(methods) > 0 {

16
main.go
View file

@ -54,7 +54,6 @@ func startApiServer(bot *telegram.TipBot) {
// append lnurl ctx functions
lnUrl := lnurl.New(bot)
s.AppendRoute("/.well-known/lnurlp/{username}", lnUrl.Handle, http.MethodGet)
// userpage server
userpage := userpage.New(bot)
s.AppendRoute("/@{username}", userpage.UserPageHandler, http.MethodGet)
@ -63,14 +62,16 @@ func startApiServer(bot *telegram.TipBot) {
hub := lndhub.New(bot)
s.AppendRoute(`/lndhub/ext/{.*}`, hub.Handle)
s.AppendRoute(`/lndhub/ext`, hub.Handle)
//s.AppendAuthorizedRoute(`/lndhub/ext/{.*}`, api.AuthTypeBearer, bot.DB.Users, hub.Handle)
//s.AppendAuthorizedRoute(`/lndhub/ext`, api.AuthTypeBearer, bot.DB.Users, hub.Handle)
// starting api service
apiService := api.Service{}
s.AppendRoute(`/api/v1/paymentstatus/{payment_hash}`, apiService.PaymentStatus, http.MethodPost)
s.AppendRoute(`/api/v1/invoicestatus/{payment_hash}`, apiService.InvoiceStatus, http.MethodPost)
s.AppendRoute(`/api/v1/payinvoice`, apiService.PayInvoice, http.MethodPost)
s.AppendRoute(`/api/v1/createinvoice`, apiService.CreateInvoice, http.MethodPost)
s.AppendRoute(`/api/v1/balance`, apiService.Balance, http.MethodGet)
s.AppendAuthorizedRoute(`/api/v1/paymentstatus/{payment_hash}`, api.AuthTypeBasic, bot.DB.Users, apiService.PaymentStatus, http.MethodPost)
s.AppendAuthorizedRoute(`/api/v1/invoicestatus/{payment_hash}`, api.AuthTypeBasic, bot.DB.Users, apiService.InvoiceStatus, http.MethodPost)
s.AppendAuthorizedRoute(`/api/v1/payinvoice`, api.AuthTypeBasic, bot.DB.Users, apiService.PayInvoice, http.MethodPost)
s.AppendAuthorizedRoute(`/api/v1/createinvoice`, api.AuthTypeBasic, bot.DB.Users, apiService.CreateInvoice, http.MethodPost)
s.AppendAuthorizedRoute(`/api/v1/balance`, api.AuthTypeBasic, bot.DB.Users, apiService.Balance, http.MethodGet)
// start internal admin server
adminService := admin.New(bot)
@ -83,6 +84,9 @@ func startApiServer(bot *telegram.TipBot) {
}
func withMiddleware(mw func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
return mw
}
func withRecovery() {
if r := recover(); r != nil {
log.Errorln("Recovered panic: ", r)