Files
Mihai Doarna d57d184d20 Auth: Remove api key endpoints (#106019)
* remove api key endpoints

* generate openapi specs

* remove methods from mock service

* remove ApiKeyDTO

* generate openapi specs

* remove apikey migration endpoints

* remove unused function
2025-06-04 17:03:06 +03:00

226 lines
6.6 KiB
Go

package apikeyimpl
import (
"context"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/tests/testsuite"
)
func TestMain(m *testing.M) {
testsuite.Run(m)
}
type getStore func(db.DB) store
type getApiKeysTestCase struct {
desc string
user identity.Requester
expectedAllNumKeys int
}
func mockTimeNow() {
var timeSeed int64
timeNow = func() time.Time {
loc := time.FixedZone("MockZoneUTC-5", -5*60*60)
fakeNow := time.Unix(timeSeed, 0).In(loc)
timeSeed++
return fakeNow
}
}
func resetTimeNow() {
timeNow = time.Now
}
func seedApiKeys(t *testing.T, store store, num int) {
t.Helper()
for i := 0; i < num; i++ {
_, err := store.AddAPIKey(context.Background(), &apikey.AddCommand{
Name: fmt.Sprintf("key:%d", i),
Key: fmt.Sprintf("key:%d", i),
OrgID: 1,
})
require.NoError(t, err)
}
}
func testIntegrationApiKeyDataAccess(t *testing.T, fn getStore) {
t.Helper()
mockTimeNow()
defer resetTimeNow()
t.Run("Testing API Key data access", func(t *testing.T) {
db := db.InitTestDB(t)
ss := fn(db)
t.Run("Given saved api key", func(t *testing.T) {
cmd := apikey.AddCommand{OrgID: 1, Name: "hello", Key: "asd"}
_, err := ss.AddAPIKey(context.Background(), &cmd)
assert.Nil(t, err)
t.Run("Should be able to get key by name", func(t *testing.T) {
query := apikey.GetByNameQuery{KeyName: "hello", OrgID: 1}
key, err := ss.GetApiKeyByName(context.Background(), &query)
assert.Nil(t, err)
assert.NotNil(t, key)
})
t.Run("Should be able to get key by hash", func(t *testing.T) {
key, err := ss.GetAPIKeyByHash(context.Background(), cmd.Key)
assert.Nil(t, err)
assert.NotNil(t, key)
})
})
t.Run("Add non expiring key", func(t *testing.T) {
cmd := apikey.AddCommand{OrgID: 1, Name: "non-expiring", Key: "asd1", SecondsToLive: 0}
_, err := ss.AddAPIKey(context.Background(), &cmd)
assert.Nil(t, err)
query := apikey.GetByNameQuery{KeyName: "non-expiring", OrgID: 1}
key, err := ss.GetApiKeyByName(context.Background(), &query)
assert.Nil(t, err)
assert.Nil(t, key.Expires)
})
t.Run("Add an expiring key", func(t *testing.T) {
// expires in one hour
cmd := apikey.AddCommand{OrgID: 1, Name: "expiring-in-an-hour", Key: "asd2", SecondsToLive: 3600}
_, err := ss.AddAPIKey(context.Background(), &cmd)
assert.Nil(t, err)
query := apikey.GetByNameQuery{KeyName: "expiring-in-an-hour", OrgID: 1}
key, err := ss.GetApiKeyByName(context.Background(), &query)
assert.Nil(t, err)
assert.True(t, *key.Expires >= timeNow().Unix())
// timeNow() has been called twice since creation; once by AddAPIKey and once by GetApiKeyByName
// therefore two seconds should be subtracted by next value returned by timeNow()
// that equals the number by which timeSeed has been advanced
then := timeNow().Add(-2 * time.Second)
expected := then.Add(1 * time.Hour).UTC().Unix()
assert.Equal(t, *key.Expires, expected)
})
t.Run("Last Used At datetime update", func(t *testing.T) {
// expires in one hour
cmd := apikey.AddCommand{OrgID: 1, Name: "last-update-at", Key: "asd3", SecondsToLive: 3600}
key, err := ss.AddAPIKey(context.Background(), &cmd)
require.NoError(t, err)
assert.Nil(t, key.LastUsedAt)
err = ss.UpdateAPIKeyLastUsedDate(context.Background(), key.ID)
require.NoError(t, err)
query := apikey.GetByNameQuery{KeyName: "last-update-at", OrgID: 1}
key, err = ss.GetApiKeyByName(context.Background(), &query)
assert.Nil(t, err)
assert.NotNil(t, key.LastUsedAt)
})
t.Run("Add a key with negative lifespan", func(t *testing.T) {
// expires in one day
cmd := apikey.AddCommand{OrgID: 1, Name: "key-with-negative-lifespan", Key: "asd3", SecondsToLive: -3600}
_, err := ss.AddAPIKey(context.Background(), &cmd)
assert.EqualError(t, err, apikey.ErrInvalidExpiration.Error())
query := apikey.GetByNameQuery{KeyName: "key-with-negative-lifespan", OrgID: 1}
_, err = ss.GetApiKeyByName(context.Background(), &query)
assert.EqualError(t, err, "invalid API key")
})
t.Run("Add keys", func(t *testing.T) {
// never expires
cmd := apikey.AddCommand{OrgID: 1, Name: "key1", Key: "key1", SecondsToLive: 0}
_, err := ss.AddAPIKey(context.Background(), &cmd)
assert.Nil(t, err)
// expires in 1s
cmd = apikey.AddCommand{OrgID: 1, Name: "key2", Key: "key2", SecondsToLive: 1}
_, err = ss.AddAPIKey(context.Background(), &cmd)
assert.Nil(t, err)
// expires in one hour
cmd = apikey.AddCommand{OrgID: 1, Name: "key3", Key: "key3", SecondsToLive: 3600}
_, err = ss.AddAPIKey(context.Background(), &cmd)
assert.Nil(t, err)
// advance mocked getTime by 1s
timeNow()
})
})
t.Run("Testing API Key errors", func(t *testing.T) {
db := db.InitTestDB(t)
ss := fn(db)
t.Run("Testing API Duplicate Key Errors", func(t *testing.T) {
t.Run("Given saved api key", func(t *testing.T) {
cmd := apikey.AddCommand{OrgID: 0, Name: "duplicate", Key: "asd"}
_, err := ss.AddAPIKey(context.Background(), &cmd)
assert.Nil(t, err)
t.Run("Add API Key with existing Org ID and Name", func(t *testing.T) {
cmd := apikey.AddCommand{OrgID: 0, Name: "duplicate", Key: "asd"}
_, err = ss.AddAPIKey(context.Background(), &cmd)
assert.EqualError(t, err, apikey.ErrDuplicate.Error())
})
})
})
})
t.Run("Testing Get API keys", func(t *testing.T) {
tests := []getApiKeysTestCase{
{
desc: "expect all keys for wildcard scope",
user: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"apikeys:read": {"apikeys:*"}},
}},
expectedAllNumKeys: 10,
},
{
desc: "expect only api keys that user have scopes for",
user: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"apikeys:read": {"apikeys:id:1", "apikeys:id:3"}},
}},
expectedAllNumKeys: 10,
},
{
desc: "expect no keys when user have no scopes",
user: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"apikeys:read": {}},
}},
expectedAllNumKeys: 10,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
db := db.InitTestDB(t, db.InitTestDBOpt{})
store := fn(db)
seedApiKeys(t, store, 10)
res, err := store.GetAllAPIKeys(context.Background(), 1)
require.NoError(t, err)
assert.Equal(t, tt.expectedAllNumKeys, len(res))
})
}
})
}