github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/service/api_key_test.go (about)

     1  package service_test
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	. "github.com/onsi/ginkgo/v2"
     8  	. "github.com/onsi/gomega"
     9  
    10  	"github.com/pyroscope-io/pyroscope/pkg/model"
    11  	"github.com/pyroscope-io/pyroscope/pkg/service"
    12  )
    13  
    14  var _ = Describe("APIKeyService", func() {
    15  	s := new(testSuite)
    16  	BeforeEach(s.BeforeEach)
    17  	AfterEach(s.AfterEach)
    18  
    19  	var svc service.APIKeyService
    20  	BeforeEach(func() {
    21  		svc = service.NewAPIKeyService(s.DB(), 0)
    22  	})
    23  
    24  	Describe("API key creation", func() {
    25  		var (
    26  			params = testCreateAPIKeyParams()
    27  			apiKey model.APIKey
    28  			key    string
    29  			err    error
    30  		)
    31  
    32  		JustBeforeEach(func() {
    33  			apiKey, key, err = svc.CreateAPIKey(context.Background(), params)
    34  		})
    35  
    36  		Context("when a new API key created", func() {
    37  			It("does not return error", func() {
    38  				Expect(err).ToNot(HaveOccurred())
    39  			})
    40  
    41  			It("should populate the fields correctly", func() {
    42  				expectAPIKeyMatches(apiKey, params)
    43  			})
    44  
    45  			It("creates a valid secret", func() {
    46  				id, secret, err := model.DecodeAPIKey(key)
    47  				Expect(err).ToNot(HaveOccurred())
    48  				Expect(id).To(Equal(apiKey.ID))
    49  				Expect(apiKey.Verify(secret)).ToNot(HaveOccurred())
    50  			})
    51  		})
    52  
    53  		Context("when API key name is already in use", func() {
    54  			BeforeEach(func() {
    55  				_, _, err = svc.CreateAPIKey(context.Background(), params)
    56  				Expect(err).ToNot(HaveOccurred())
    57  			})
    58  
    59  			It("returns validation error", func() {
    60  				Expect(model.IsValidationError(err)).To(BeTrue())
    61  			})
    62  		})
    63  
    64  		Context("when parameters are invalid", func() {
    65  			BeforeEach(func() {
    66  				params = model.CreateAPIKeyParams{}
    67  			})
    68  
    69  			It("returns validation error", func() {
    70  				Expect(model.IsValidationError(err)).To(BeTrue())
    71  			})
    72  		})
    73  	})
    74  
    75  	Describe("API key retrieval", func() {
    76  		Context("when an existing API key is queried", func() {
    77  			It("can be found by id", func() {
    78  				params := testCreateAPIKeyParams()
    79  				k, _, err := svc.CreateAPIKey(context.Background(), params)
    80  				Expect(err).ToNot(HaveOccurred())
    81  				actual, err := svc.FindAPIKeyByID(context.Background(), k.ID)
    82  				Expect(err).ToNot(HaveOccurred())
    83  				expectAPIKeyMatches(actual, params)
    84  			})
    85  		})
    86  
    87  		Context("when a non-existing API key is queried", func() {
    88  			It("returns ErrAPIKeyNotFound error of NotFoundError type", func() {
    89  				_, err := svc.FindAPIKeyByID(context.Background(), 13)
    90  				Expect(err).To(MatchError(model.ErrAPIKeyNotFound))
    91  			})
    92  		})
    93  	})
    94  
    95  	Describe("API keys retrieval", func() {
    96  		var (
    97  			params = []model.CreateAPIKeyParams{
    98  				{Name: "key-a", Role: model.AdminRole},
    99  				{Name: "key-b", Role: model.ReadOnlyRole},
   100  			}
   101  			apiKeys []model.APIKey
   102  			err     error
   103  		)
   104  
   105  		JustBeforeEach(func() {
   106  			apiKeys, err = svc.GetAllAPIKeys(context.Background())
   107  		})
   108  
   109  		Context("when all API keys are queried", func() {
   110  			BeforeEach(func() {
   111  				for _, user := range params {
   112  					_, _, err = svc.CreateAPIKey(context.Background(), user)
   113  					Expect(err).ToNot(HaveOccurred())
   114  				}
   115  			})
   116  
   117  			It("does not return error", func() {
   118  				Expect(err).ToNot(HaveOccurred())
   119  			})
   120  
   121  			It("returns all keys", func() {
   122  				apiKeyA, err := svc.FindAPIKeyByName(context.Background(), params[0].Name)
   123  				Expect(err).ToNot(HaveOccurred())
   124  				apiKeyB, err := svc.FindAPIKeyByName(context.Background(), params[1].Name)
   125  				Expect(err).ToNot(HaveOccurred())
   126  				Expect(apiKeys).To(ConsistOf(apiKeyA, apiKeyB))
   127  			})
   128  		})
   129  
   130  		Context("when no API keys exist", func() {
   131  			It("returns no error", func() {
   132  				Expect(err).ToNot(HaveOccurred())
   133  				Expect(apiKeys).To(BeEmpty())
   134  			})
   135  		})
   136  	})
   137  
   138  	Describe("API key delete", func() {
   139  		var (
   140  			params = []model.CreateAPIKeyParams{
   141  				{Name: "key-a", Role: model.AdminRole},
   142  				{Name: "key-b", Role: model.ReadOnlyRole},
   143  			}
   144  			apiKeys []model.APIKey
   145  			err     error
   146  		)
   147  
   148  		JustBeforeEach(func() {
   149  			err = svc.DeleteAPIKeyByID(context.Background(), apiKeys[0].ID)
   150  		})
   151  
   152  		Context("when existing API key deleted", func() {
   153  			BeforeEach(func() {
   154  				apiKeys = apiKeys[:0]
   155  				for _, p := range params {
   156  					apiKey, _, err := svc.CreateAPIKey(context.Background(), p)
   157  					Expect(err).ToNot(HaveOccurred())
   158  					apiKeys = append(apiKeys, apiKey)
   159  				}
   160  			})
   161  
   162  			It("does not return error", func() {
   163  				Expect(err).ToNot(HaveOccurred())
   164  			})
   165  
   166  			It("removes API key from the database", func() {
   167  				_, err = svc.FindAPIKeyByName(context.Background(), apiKeys[0].Name)
   168  				Expect(err).To(MatchError(model.ErrAPIKeyNotFound))
   169  			})
   170  
   171  			It("does not affect other API keys", func() {
   172  				_, err = svc.FindAPIKeyByName(context.Background(), apiKeys[1].Name)
   173  				Expect(err).ToNot(HaveOccurred())
   174  			})
   175  
   176  			It("allows API key with the same name to be created", func() {
   177  				_, _, err = svc.CreateAPIKey(context.Background(), params[0])
   178  				Expect(err).ToNot(HaveOccurred())
   179  			})
   180  		})
   181  
   182  		Context("when non-existing API key deleted", func() {
   183  			It("does not return error", func() {
   184  				Expect(err).ToNot(HaveOccurred())
   185  			})
   186  		})
   187  	})
   188  })
   189  
   190  func testCreateAPIKeyParams() model.CreateAPIKeyParams {
   191  	expiresAt := time.Date(3000, 12, 10, 4, 14, 0, 0, time.UTC)
   192  	return model.CreateAPIKeyParams{
   193  		Name:      "johndoe",
   194  		Role:      model.ReadOnlyRole,
   195  		ExpiresAt: &expiresAt,
   196  	}
   197  }
   198  
   199  func expectAPIKeyMatches(apiKey model.APIKey, params model.CreateAPIKeyParams) {
   200  	Expect(apiKey.Name).To(Equal(params.Name))
   201  	Expect(apiKey.Role).To(Equal(params.Role))
   202  	Expect(apiKey.ExpiresAt).To(Equal(params.ExpiresAt))
   203  	Expect(apiKey.CreatedAt).ToNot(BeZero())
   204  	Expect(apiKey.LastSeenAt).To(BeZero())
   205  }