...

Source file src/github.com/cybertec-postgresql/pgwatch/v5/internal/webserver/jwt.go

Documentation: github.com/cybertec-postgresql/pgwatch/v5/internal/webserver

     1  package webserver
     2  
     3  import (
     4  	"errors"
     5  	"net/http"
     6  	"time"
     7  
     8  	jsoniter "github.com/json-iterator/go"
     9  
    10  	"github.com/golang-jwt/jwt/v5"
    11  )
    12  
    13  type loginReq struct {
    14  	Username string `json:"user"`
    15  	Password string `json:"password"`
    16  }
    17  
    18  func (s *WebUIServer) IsCorrectPassword(lr loginReq) bool {
    19  	return (s.WebUser+s.WebPassword == "") ||
    20  		(s.WebUser == lr.Username && s.WebPassword == lr.Password)
    21  }
    22  
    23  func (s *WebUIServer) handleLogin(w http.ResponseWriter, r *http.Request) {
    24  	var (
    25  		err   error
    26  		lr    loginReq
    27  		token string
    28  	)
    29  
    30  	defer func() {
    31  		if err != nil {
    32  			http.Error(w, err.Error(), http.StatusInternalServerError)
    33  		}
    34  	}()
    35  
    36  	switch r.Method {
    37  	case "POST":
    38  		if err = jsoniter.ConfigFastest.NewDecoder(r.Body).Decode(&lr); err != nil {
    39  			return
    40  		}
    41  		if !s.IsCorrectPassword(lr) {
    42  			http.Error(w, "can not authenticate this user", http.StatusUnauthorized)
    43  			return
    44  		}
    45  		if token, err = generateJWT(lr.Username); err != nil {
    46  			return
    47  		}
    48  		_, err = w.Write([]byte(token))
    49  
    50  	default:
    51  		w.Header().Set("Allow", "POST")
    52  		http.Error(w, "only POST method is allowed", http.StatusMethodNotAllowed)
    53  		return
    54  	}
    55  }
    56  
    57  type EnsureAuth struct {
    58  	handler http.HandlerFunc
    59  }
    60  
    61  func (ea *EnsureAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    62  	if err := validateToken(r); err != nil {
    63  		http.Error(w, err.Error(), http.StatusUnauthorized)
    64  		return
    65  	}
    66  	ea.handler(w, r)
    67  }
    68  
    69  func NewEnsureAuth(handlerToWrap http.HandlerFunc) *EnsureAuth {
    70  	return &EnsureAuth{handlerToWrap}
    71  }
    72  
    73  var sampleSecretKey = []byte("5m3R7K4754p4m")
    74  
    75  func generateJWT(username string) (string, error) {
    76  	token := jwt.New(jwt.SigningMethodHS256)
    77  	claims := token.Claims.(jwt.MapClaims)
    78  
    79  	claims["authorized"] = true
    80  	claims["username"] = username
    81  	claims["exp"] = time.Now().Add(time.Hour * 8).Unix()
    82  
    83  	return token.SignedString(sampleSecretKey)
    84  }
    85  
    86  func validateToken(r *http.Request) (err error) {
    87  	var t string
    88  	if r.Header["Token"] == nil {
    89  		t = r.URL.Query().Get("Token")
    90  	} else {
    91  		t = r.Header["Token"][0]
    92  	}
    93  	if t == "" {
    94  		return errors.New("can not find token in header")
    95  	}
    96  
    97  	_, err = jwt.Parse(t,
    98  		func(_ *jwt.Token) (any, error) {
    99  			return sampleSecretKey, nil
   100  		},
   101  		jwt.WithExpirationRequired(),
   102  		jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
   103  	return err
   104  }
   105