mirror of
https://github.com/ChuckNorrison/LightningTipBot.git
synced 2026-08-13 12:33:14 +02:00
125 lines
3.9 KiB
Go
125 lines
3.9 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"strings"
|
|
|
|
"github.com/LightningTipBot/LightningTipBot/internal/lnbits"
|
|
"gorm.io/gorm"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func LoggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
log.WithFields(log.Fields{"module": "api", "method": r.Method, "path": r.URL.Path, "request": dump(r)}).Info("incoming api request")
|
|
next.ServeHTTP(w, r)
|
|
}
|
|
}
|
|
|
|
type AuthType struct {
|
|
Type string
|
|
Decoder func(s string) ([]byte, error)
|
|
}
|
|
|
|
var AuthTypeBasic = AuthType{Type: "Basic"}
|
|
var AuthTypeBearerBase64 = AuthType{Type: "Bearer", Decoder: base64.StdEncoding.DecodeString}
|
|
var AuthTypeNone = AuthType{}
|
|
|
|
// invoice key or admin key requirement
|
|
type AccessKeyType struct {
|
|
Type string
|
|
}
|
|
|
|
var AccessKeyTypeInvoice = AccessKeyType{Type: "invoice"}
|
|
var AccessKeyTypeAdmin = AccessKeyType{Type: "admin"}
|
|
var AccessKeyTypeNone = AccessKeyType{Type: "none"} // no authorization required
|
|
|
|
func AuthorizationMiddleware(database *gorm.DB, authType AuthType, accessType AccessKeyType, next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if accessType.Type == "none" {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
auth := r.Header.Get("Authorization")
|
|
// check if the user is banned
|
|
if auth == "" {
|
|
w.WriteHeader(401)
|
|
log.WithFields(log.Fields{"module": "api", "func": "AuthorizationMiddleware"}).Warn("no auth")
|
|
return
|
|
}
|
|
_, password, ok := parseAuth(authType, auth)
|
|
if !ok {
|
|
w.WriteHeader(401)
|
|
return
|
|
}
|
|
// first we make sure that the password is not already "banned_"
|
|
if strings.Contains(password, "_") || strings.HasPrefix(password, "banned_") {
|
|
w.WriteHeader(401)
|
|
log.WithFields(log.Fields{"module": "api", "func": "AuthorizationMiddleware", "user": password}).Warn("Banned user. Not forwarding request")
|
|
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{}
|
|
var tx *gorm.DB
|
|
if accessType.Type == "admin" {
|
|
tx = database.Where("wallet_adminkey = ? COLLATE NOCASE", password).First(user)
|
|
} else if accessType.Type == "invoice" {
|
|
tx = database.Where("wallet_inkey = ? COLLATE NOCASE", password).First(user)
|
|
} else {
|
|
log.WithFields(log.Fields{"module": "api", "func": "AuthorizationMiddleware", "user": password, "error": tx.Error}).
|
|
Warnf("route without access type")
|
|
w.WriteHeader(401)
|
|
return
|
|
}
|
|
if tx.Error != nil {
|
|
log.WithFields(log.Fields{"module": "api", "func": "AuthorizationMiddleware", "user": password, "error": tx.Error}).
|
|
Warnf("could not load access key")
|
|
w.WriteHeader(401)
|
|
return
|
|
}
|
|
log.WithFields(log.Fields{"module": "api", "func": "AuthorizationMiddleware", "path": fmt.Sprintf("%s %s%s", r.Method, r.URL.Path, r.URL.RawQuery), "user": user.GetUserStr()}).
|
|
Debugf("Loaded Api user")
|
|
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
|
|
}
|
|
if authType.Decoder != nil {
|
|
c, err := authType.Decoder(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 auth[len(prefix):], auth[len(prefix):], true
|
|
|
|
}
|
|
return parse(fmt.Sprintf("%s ", authType.Type))
|
|
|
|
}
|
|
|
|
func dump(r *http.Request) string {
|
|
x, err := httputil.DumpRequest(r, true)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(x)
|
|
}
|