restapi_auth_jwt_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package httpservers
  2. import (
  3. "context"
  4. "crypto/rand"
  5. "crypto/rsa"
  6. "crypto/x509"
  7. "encoding/pem"
  8. "fmt"
  9. config "github.com/OliveTin/OliveTin/internal/config"
  10. "github.com/golang-jwt/jwt/v4"
  11. "github.com/stretchr/testify/assert"
  12. "io"
  13. "net/http"
  14. "os"
  15. "testing"
  16. "time"
  17. )
  18. func createKeys(t *testing.T) (*rsa.PrivateKey, string) {
  19. tmpFile, _ := os.CreateTemp(os.TempDir(), "olivetin-jwt-")
  20. fmt.Println("Created File: " + tmpFile.Name())
  21. privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
  22. pubKey := &privateKey.PublicKey
  23. // https://stackoverflow.com/questions/13555085/save-and-load-crypto-rsa-privatekey-to-and-from-the-disk
  24. pkixPubKey, _ := x509.MarshalPKIXPublicKey(pubKey)
  25. pubPem := pem.EncodeToMemory(
  26. &pem.Block{
  27. Type: "RSA PUBLIC KEY",
  28. Bytes: pkixPubKey,
  29. },
  30. )
  31. if err := os.WriteFile(tmpFile.Name(), pubPem, 0755); err != nil {
  32. t.Fatalf("error when dumping pubKey: %s \n", err)
  33. }
  34. return privateKey, tmpFile.Name()
  35. }
  36. func testJwkValidation(t *testing.T, expire int64, expectCode int) {
  37. privateKey, publicKeyPath := createKeys(t)
  38. defer os.Remove(publicKeyPath)
  39. cfg := config.DefaultConfig()
  40. cfg.AuthJwtPubKeyPath = publicKeyPath
  41. cfg.AuthJwtClaimUsername = "sub"
  42. cfg.AuthJwtClaimUserGroup = "olivetinGroup"
  43. cfg.AuthJwtCookieName = "authorization_token"
  44. SetGlobalRestConfig(cfg) // ugly, setting global var, we should pass configs as params to modules... :/
  45. token := jwt.New(jwt.SigningMethodRS256)
  46. claims := token.Claims.(jwt.MapClaims)
  47. claims["nbf"] = time.Now().Unix() - 1000
  48. claims["exp"] = time.Now().Unix() + expire
  49. claims["sub"] = "test"
  50. claims["olivetinGroup"] = "test"
  51. tokenStr, _ := token.SignedString(privateKey)
  52. mux := newMux()
  53. mux.HandlePath("GET", "/", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
  54. username, usergroup := parseJwtCookie(r)
  55. if username == "" {
  56. w.WriteHeader(403)
  57. }
  58. w.Write([]byte(fmt.Sprintf("username=%v, usergroup=%v", username, usergroup)))
  59. })
  60. srv := setupTestingServer(mux, t)
  61. req, client := newReq("")
  62. req.AddCookie(&http.Cookie{
  63. Name: "authorization_token",
  64. Value: tokenStr,
  65. MaxAge: 300,
  66. })
  67. res, err := client.Do(req)
  68. if err != nil {
  69. t.Fatalf("Client err: %+v", err)
  70. } else {
  71. defer res.Body.Close()
  72. assert.Equal(t, expectCode, res.StatusCode)
  73. body, _ := io.ReadAll(res.Body)
  74. fmt.Println(string(body))
  75. }
  76. err = srv.Shutdown(context.TODO())
  77. if err != nil {
  78. t.Fatalf("Server shutdown error: %+v", err)
  79. }
  80. }
  81. func TestJWTSignatureVerificationSucceeds(t *testing.T) {
  82. testJwkValidation(t, 1000, 200)
  83. }
  84. func TestJWTSignatureVerificationFails(t *testing.T) {
  85. testJwkValidation(t, -500, 403)
  86. }
  87. func TestJWTHeader(t *testing.T) {
  88. privateKey, publicKeyPath := createKeys(t)
  89. defer os.Remove(publicKeyPath)
  90. cfg := config.DefaultConfig()
  91. cfg.AuthJwtPubKeyPath = publicKeyPath
  92. cfg.AuthJwtClaimUsername = "sub"
  93. cfg.AuthJwtClaimUserGroup = "olivetinGroup"
  94. cfg.AuthJwtHeader = "Authorization"
  95. SetGlobalRestConfig(cfg) // Ugly, setting global var, we should pass configs as params to modules... :/
  96. token := jwt.New(jwt.SigningMethodRS256)
  97. claims := token.Claims.(jwt.MapClaims)
  98. claims["nbf"] = time.Now().Unix() - 1000
  99. claims["exp"] = time.Now().Unix() + 2000
  100. claims["sub"] = "test"
  101. claims["olivetinGroup"] = []string{"test", "test2"}
  102. tokenStr, _ := token.SignedString(privateKey)
  103. mux := newMux()
  104. mux.HandlePath("GET", "/", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
  105. username, usergroup := parseJwtHeader(r)
  106. if username == "" {
  107. w.WriteHeader(403)
  108. }
  109. assert.Equal(t, "test", username)
  110. assert.Equal(t, "test test2", usergroup)
  111. w.Write([]byte(fmt.Sprintf("username=%v, usergroup=%v", username, usergroup)))
  112. })
  113. srv := setupTestingServer(mux, t)
  114. req, client := newReq("")
  115. req.Header.Set("Authorization", "Bearer "+tokenStr)
  116. res, err := client.Do(req)
  117. if err != nil {
  118. t.Fatalf("Client err: %+v", err)
  119. } else {
  120. defer res.Body.Close()
  121. assert.Equal(t, 200, res.StatusCode)
  122. body, _ := io.ReadAll(res.Body)
  123. fmt.Println(string(body))
  124. }
  125. srv.Shutdown(context.TODO())
  126. }