Files
netbird/management/server/http/testing/integration/setupkeys_handler_integration_test.go
mlsmaycon 6a82a39ced [management] Expect the rejection in the setup key integration tests
"Create Setup Key as on-off with more than one usage" asserted the old
behaviour directly: a key was created, and the response reported a usage
limit of 1 rather than the 3 that was asked for. That is the case this
change refuses, so the expectation becomes the rejection.

The case keeps its place in the table rather than being deleted, since it
is the one that says what happens when the request contradicts the key
type.
2026-08-16 02:43:37 +00:00

1143 lines
31 KiB
Go

//go:build integration
package integration
import (
"encoding/json"
"net/http"
"net/http/httptest"
"sort"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/netbirdio/netbird/management/server/http/testing/testing_tools"
"github.com/netbirdio/netbird/management/server/http/testing/testing_tools/channel"
"github.com/netbirdio/netbird/shared/management/http/api"
)
func Test_SetupKeys_Create(t *testing.T) {
truePointer := true
users := []struct {
name string
userId string
expectResponse bool
}{
{
name: "Regular user",
userId: testing_tools.TestUserId,
expectResponse: false,
},
{
name: "Admin user",
userId: testing_tools.TestAdminId,
expectResponse: true,
},
{
name: "Owner user",
userId: testing_tools.TestOwnerId,
expectResponse: true,
},
{
name: "Regular service user",
userId: testing_tools.TestServiceUserId,
expectResponse: false,
},
{
name: "Admin service user",
userId: testing_tools.TestServiceAdminId,
expectResponse: true,
},
{
name: "Blocked user",
userId: testing_tools.BlockedUserId,
expectResponse: false,
},
{
name: "Other user",
userId: testing_tools.OtherUserId,
expectResponse: false,
},
{
name: "Invalid token",
userId: testing_tools.InvalidToken,
expectResponse: false,
},
}
tt := []struct {
name string
expectedStatus int
expectedResponse *api.SetupKey
requestBody *api.CreateSetupKeyRequest
requestType string
requestPath string
userId string
}{
{
name: "Create Setup Key",
requestType: http.MethodPost,
requestPath: "/api/setup-keys",
requestBody: &api.CreateSetupKeyRequest{
AutoGroups: nil,
ExpiresIn: testing_tools.ExpiresIn,
Name: testing_tools.NewKeyName,
Type: "reusable",
UsageLimit: 0,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.NewKeyName,
Revoked: false,
State: "valid",
Type: "reusable",
UpdatedAt: time.Now(),
UsageLimit: 0,
UsedTimes: 0,
Valid: true,
},
},
{
name: "Create Setup Key with already existing name",
requestType: http.MethodPost,
requestPath: "/api/setup-keys",
requestBody: &api.CreateSetupKeyRequest{
AutoGroups: nil,
ExpiresIn: testing_tools.ExpiresIn,
Name: testing_tools.ExistingKeyName,
Type: "one-off",
UsageLimit: 0,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: false,
State: "valid",
Type: "one-off",
UpdatedAt: time.Now(),
UsageLimit: 1,
UsedTimes: 0,
Valid: true,
},
},
{
// The key used to be created anyway, with its usage limit quietly
// reduced to 1, so the caller was told a key they had not asked for
// was what they asked for.
name: "Create Setup Key as one-off with more than one usage",
requestType: http.MethodPost,
requestPath: "/api/setup-keys",
requestBody: &api.CreateSetupKeyRequest{
AutoGroups: nil,
ExpiresIn: testing_tools.ExpiresIn,
Name: testing_tools.NewKeyName,
Type: "one-off",
UsageLimit: 3,
},
expectedStatus: http.StatusUnprocessableEntity,
},
{
name: "Create Setup Key with expiration in the past",
requestType: http.MethodPost,
requestPath: "/api/setup-keys",
requestBody: &api.CreateSetupKeyRequest{
AutoGroups: nil,
ExpiresIn: -testing_tools.ExpiresIn,
Name: testing_tools.NewKeyName,
Type: "one-off",
UsageLimit: 0,
},
expectedStatus: http.StatusUnprocessableEntity,
expectedResponse: nil,
},
{
name: "Create Setup Key with AutoGroups that do exist",
requestType: http.MethodPost,
requestPath: "/api/setup-keys",
requestBody: &api.CreateSetupKeyRequest{
AutoGroups: []string{testing_tools.TestGroupId},
ExpiresIn: testing_tools.ExpiresIn,
Name: testing_tools.NewKeyName,
Type: "reusable",
UsageLimit: 1,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.NewKeyName,
Revoked: false,
State: "valid",
Type: "reusable",
UpdatedAt: time.Now(),
UsageLimit: 1,
UsedTimes: 0,
Valid: true,
},
},
{
name: "Create Setup Key for ephemeral peers",
requestType: http.MethodPost,
requestPath: "/api/setup-keys",
requestBody: &api.CreateSetupKeyRequest{
AutoGroups: []string{},
ExpiresIn: testing_tools.ExpiresIn,
Name: testing_tools.NewKeyName,
Type: "reusable",
Ephemeral: &truePointer,
UsageLimit: 1,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{},
Ephemeral: true,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.NewKeyName,
Revoked: false,
State: "valid",
Type: "reusable",
UpdatedAt: time.Now(),
UsageLimit: 1,
UsedTimes: 0,
Valid: true,
},
},
{
name: "Create Setup Key with AutoGroups that do not exist",
requestType: http.MethodPost,
requestPath: "/api/setup-keys",
requestBody: &api.CreateSetupKeyRequest{
AutoGroups: []string{"someGroupID"},
ExpiresIn: testing_tools.ExpiresIn,
Name: testing_tools.NewKeyName,
Type: "reusable",
UsageLimit: 0,
},
expectedStatus: http.StatusUnprocessableEntity,
expectedResponse: nil,
},
{
name: "Create Setup Key with nil AutoGroups",
requestType: http.MethodPost,
requestPath: "/api/setup-keys",
requestBody: &api.CreateSetupKeyRequest{
AutoGroups: nil,
ExpiresIn: testing_tools.ExpiresIn,
Name: testing_tools.NewKeyName,
Type: "reusable",
UsageLimit: 0,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.NewKeyName,
Revoked: false,
State: "valid",
Type: "reusable",
UpdatedAt: time.Now(),
UsageLimit: 0,
UsedTimes: 0,
Valid: true,
},
},
}
for _, tc := range tt {
for _, user := range users {
t.Run(user.name+" - "+tc.name, func(t *testing.T) {
apiHandler, am, done := channel.BuildApiBlackBoxWithDBState(t, "../testdata/setup_keys.sql", nil, true)
body, err := json.Marshal(tc.requestBody)
if err != nil {
t.Fatalf("Failed to marshal request body: %v", err)
}
req := testing_tools.BuildRequest(t, body, tc.requestType, tc.requestPath, user.userId)
recorder := httptest.NewRecorder()
apiHandler.ServeHTTP(recorder, req)
content, expectResponse := testing_tools.ReadResponse(t, recorder, tc.expectedStatus, user.expectResponse)
if !expectResponse {
return
}
got := &api.SetupKey{}
if err := json.Unmarshal(content, &got); err != nil {
t.Fatalf("Sent content is not in correct json format; %v", err)
}
gotID := got.Id
validateCreatedKey(t, tc.expectedResponse, got)
// Verify setup key exists in DB via gorm
db := testing_tools.GetDB(t, am.GetStore())
dbKey := testing_tools.VerifySetupKeyInDB(t, db, gotID)
assert.Equal(t, tc.expectedResponse.Name, dbKey.Name)
assert.Equal(t, tc.expectedResponse.Revoked, dbKey.Revoked)
assert.Equal(t, tc.expectedResponse.UsageLimit, dbKey.UsageLimit)
select {
case <-done:
case <-time.After(time.Second):
t.Error("timeout waiting for peerShouldNotReceiveUpdate")
}
})
}
}
}
func Test_SetupKeys_Update(t *testing.T) {
users := []struct {
name string
userId string
expectResponse bool
}{
{
name: "Regular user",
userId: testing_tools.TestUserId,
expectResponse: false,
},
{
name: "Admin user",
userId: testing_tools.TestAdminId,
expectResponse: true,
},
{
name: "Owner user",
userId: testing_tools.TestOwnerId,
expectResponse: true,
},
{
name: "Regular service user",
userId: testing_tools.TestServiceUserId,
expectResponse: false,
},
{
name: "Admin service user",
userId: testing_tools.TestServiceAdminId,
expectResponse: true,
},
{
name: "Blocked user",
userId: testing_tools.BlockedUserId,
expectResponse: false,
},
{
name: "Other user",
userId: testing_tools.OtherUserId,
expectResponse: false,
},
{
name: "Invalid token",
userId: testing_tools.InvalidToken,
expectResponse: false,
},
}
tt := []struct {
name string
expectedStatus int
expectedResponse *api.SetupKey
requestBody *api.SetupKeyRequest
requestType string
requestPath string
requestId string
}{
{
name: "Add existing Group to existing Setup Key",
requestType: http.MethodPut,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.TestKeyId,
requestBody: &api.SetupKeyRequest{
AutoGroups: []string{testing_tools.TestGroupId, testing_tools.NewGroupId},
Revoked: false,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId, testing_tools.NewGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: false,
State: "valid",
Type: "one-off",
UpdatedAt: time.Now(),
UsageLimit: 1,
UsedTimes: 0,
Valid: true,
},
},
{
name: "Add non-existing Group to existing Setup Key",
requestType: http.MethodPut,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.TestKeyId,
requestBody: &api.SetupKeyRequest{
AutoGroups: []string{testing_tools.TestGroupId, "someGroupId"},
Revoked: false,
},
expectedStatus: http.StatusUnprocessableEntity,
expectedResponse: nil,
},
{
name: "Add existing Group to non-existing Setup Key",
requestType: http.MethodPut,
requestPath: "/api/setup-keys/{id}",
requestId: "someId",
requestBody: &api.SetupKeyRequest{
AutoGroups: []string{testing_tools.TestGroupId, testing_tools.NewGroupId},
Revoked: false,
},
expectedStatus: http.StatusNotFound,
expectedResponse: nil,
},
{
name: "Remove existing Group from existing Setup Key",
requestType: http.MethodPut,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.TestKeyId,
requestBody: &api.SetupKeyRequest{
AutoGroups: []string{},
Revoked: false,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: false,
State: "valid",
Type: "one-off",
UpdatedAt: time.Now(),
UsageLimit: 1,
UsedTimes: 0,
Valid: true,
},
},
{
name: "Remove existing Group to non-existing Setup Key",
requestType: http.MethodPut,
requestPath: "/api/setup-keys/{id}",
requestId: "someID",
requestBody: &api.SetupKeyRequest{
AutoGroups: []string{},
Revoked: false,
},
expectedStatus: http.StatusNotFound,
expectedResponse: nil,
},
{
name: "Revoke existing valid Setup Key",
requestType: http.MethodPut,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.TestKeyId,
requestBody: &api.SetupKeyRequest{
AutoGroups: []string{testing_tools.TestGroupId},
Revoked: true,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: true,
State: "revoked",
Type: "one-off",
UpdatedAt: time.Now(),
UsageLimit: 1,
UsedTimes: 0,
Valid: false,
},
},
{
name: "Revoke existing revoked Setup Key",
requestType: http.MethodPut,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.RevokedKeyId,
requestBody: &api.SetupKeyRequest{
AutoGroups: []string{testing_tools.TestGroupId},
Revoked: true,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: true,
State: "revoked",
Type: "reusable",
UpdatedAt: time.Now(),
UsageLimit: 3,
UsedTimes: 0,
Valid: false,
},
},
{
name: "Un-Revoke existing revoked Setup Key",
requestType: http.MethodPut,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.RevokedKeyId,
requestBody: &api.SetupKeyRequest{
AutoGroups: []string{testing_tools.TestGroupId},
Revoked: false,
},
expectedStatus: http.StatusUnprocessableEntity,
expectedResponse: nil,
},
{
name: "Revoke existing expired Setup Key",
requestType: http.MethodPut,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.ExpiredKeyId,
requestBody: &api.SetupKeyRequest{
AutoGroups: []string{testing_tools.TestGroupId},
Revoked: true,
},
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: true,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: true,
State: "expired",
Type: "reusable",
UpdatedAt: time.Now(),
UsageLimit: 5,
UsedTimes: 1,
Valid: false,
},
},
}
for _, tc := range tt {
for _, user := range users {
t.Run(user.name+" - "+tc.name, func(t *testing.T) {
apiHandler, am, done := channel.BuildApiBlackBoxWithDBState(t, "../testdata/setup_keys.sql", nil, true)
body, err := json.Marshal(tc.requestBody)
if err != nil {
t.Fatalf("Failed to marshal request body: %v", err)
}
req := testing_tools.BuildRequest(t, body, tc.requestType, strings.Replace(tc.requestPath, "{id}", tc.requestId, 1), user.userId)
recorder := httptest.NewRecorder()
apiHandler.ServeHTTP(recorder, req)
content, expectResponse := testing_tools.ReadResponse(t, recorder, tc.expectedStatus, user.expectResponse)
if !expectResponse {
return
}
got := &api.SetupKey{}
if err := json.Unmarshal(content, &got); err != nil {
t.Fatalf("Sent content is not in correct json format; %v", err)
}
gotID := got.Id
gotRevoked := got.Revoked
gotUsageLimit := got.UsageLimit
validateCreatedKey(t, tc.expectedResponse, got)
// Verify updated setup key in DB via gorm
db := testing_tools.GetDB(t, am.GetStore())
dbKey := testing_tools.VerifySetupKeyInDB(t, db, gotID)
assert.Equal(t, gotRevoked, dbKey.Revoked)
assert.Equal(t, gotUsageLimit, dbKey.UsageLimit)
select {
case <-done:
case <-time.After(time.Second):
t.Error("timeout waiting for peerShouldNotReceiveUpdate")
}
})
}
}
}
func Test_SetupKeys_Get(t *testing.T) {
users := []struct {
name string
userId string
expectResponse bool
}{
{
name: "Regular user",
userId: testing_tools.TestUserId,
expectResponse: false,
},
{
name: "Admin user",
userId: testing_tools.TestAdminId,
expectResponse: true,
},
{
name: "Owner user",
userId: testing_tools.TestOwnerId,
expectResponse: true,
},
{
name: "Regular service user",
userId: testing_tools.TestServiceUserId,
expectResponse: false,
},
{
name: "Admin service user",
userId: testing_tools.TestServiceAdminId,
expectResponse: true,
},
{
name: "Blocked user",
userId: testing_tools.BlockedUserId,
expectResponse: false,
},
{
name: "Other user",
userId: testing_tools.OtherUserId,
expectResponse: false,
},
{
name: "Invalid token",
userId: testing_tools.InvalidToken,
expectResponse: false,
},
}
tt := []struct {
name string
expectedStatus int
expectedResponse *api.SetupKey
requestType string
requestPath string
requestId string
}{
{
name: "Get existing valid Setup Key",
requestType: http.MethodGet,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.TestKeyId,
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: false,
State: "valid",
Type: "one-off",
UpdatedAt: time.Date(2021, time.August, 19, 20, 46, 20, 0, time.UTC),
UsageLimit: 1,
UsedTimes: 0,
Valid: true,
},
},
{
name: "Get existing expired Setup Key",
requestType: http.MethodGet,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.ExpiredKeyId,
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: true,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: false,
State: "expired",
Type: "reusable",
UpdatedAt: time.Date(2021, time.August, 19, 20, 46, 20, 0, time.UTC),
UsageLimit: 5,
UsedTimes: 1,
Valid: false,
},
},
{
name: "Get existing revoked Setup Key",
requestType: http.MethodGet,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.RevokedKeyId,
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: true,
State: "revoked",
Type: "reusable",
UpdatedAt: time.Date(2021, time.August, 19, 20, 46, 20, 0, time.UTC),
UsageLimit: 3,
UsedTimes: 0,
Valid: false,
},
},
{
name: "Get non-existing Setup Key",
requestType: http.MethodGet,
requestPath: "/api/setup-keys/{id}",
requestId: "someId",
expectedStatus: http.StatusNotFound,
expectedResponse: nil,
},
}
for _, tc := range tt {
for _, user := range users {
t.Run(tc.name, func(t *testing.T) {
apiHandler, am, done := channel.BuildApiBlackBoxWithDBState(t, "../testdata/setup_keys.sql", nil, true)
req := testing_tools.BuildRequest(t, []byte{}, tc.requestType, strings.Replace(tc.requestPath, "{id}", tc.requestId, 1), user.userId)
recorder := httptest.NewRecorder()
apiHandler.ServeHTTP(recorder, req)
content, expectResponse := testing_tools.ReadResponse(t, recorder, tc.expectedStatus, user.expectResponse)
if !expectResponse {
return
}
got := &api.SetupKey{}
if err := json.Unmarshal(content, &got); err != nil {
t.Fatalf("Sent content is not in correct json format; %v", err)
}
gotID := got.Id
gotName := got.Name
gotRevoked := got.Revoked
validateCreatedKey(t, tc.expectedResponse, got)
// Verify setup key in DB via gorm
db := testing_tools.GetDB(t, am.GetStore())
dbKey := testing_tools.VerifySetupKeyInDB(t, db, gotID)
assert.Equal(t, gotName, dbKey.Name)
assert.Equal(t, gotRevoked, dbKey.Revoked)
select {
case <-done:
case <-time.After(time.Second):
t.Error("timeout waiting for peerShouldNotReceiveUpdate")
}
})
}
}
}
func Test_SetupKeys_GetAll(t *testing.T) {
users := []struct {
name string
userId string
expectResponse bool
}{
{
name: "Regular user",
userId: testing_tools.TestUserId,
expectResponse: false,
},
{
name: "Admin user",
userId: testing_tools.TestAdminId,
expectResponse: true,
},
{
name: "Owner user",
userId: testing_tools.TestOwnerId,
expectResponse: true,
},
{
name: "Regular service user",
userId: testing_tools.TestServiceUserId,
expectResponse: false,
},
{
name: "Admin service user",
userId: testing_tools.TestServiceAdminId,
expectResponse: true,
},
{
name: "Blocked user",
userId: testing_tools.BlockedUserId,
expectResponse: false,
},
{
name: "Other user",
userId: testing_tools.OtherUserId,
expectResponse: false,
},
{
name: "Invalid token",
userId: testing_tools.InvalidToken,
expectResponse: false,
},
}
tt := []struct {
name string
expectedStatus int
expectedResponse []*api.SetupKey
requestType string
requestPath string
}{
{
name: "Get all Setup Keys",
requestType: http.MethodGet,
requestPath: "/api/setup-keys",
expectedStatus: http.StatusOK,
expectedResponse: []*api.SetupKey{
{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: false,
State: "valid",
Type: "one-off",
UpdatedAt: time.Date(2021, time.August, 19, 20, 46, 20, 0, time.UTC),
UsageLimit: 1,
UsedTimes: 0,
Valid: true,
},
{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: true,
State: "revoked",
Type: "reusable",
UpdatedAt: time.Date(2021, time.August, 19, 20, 46, 20, 0, time.UTC),
UsageLimit: 3,
UsedTimes: 0,
Valid: false,
},
{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: true,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: false,
State: "expired",
Type: "reusable",
UpdatedAt: time.Date(2021, time.August, 19, 20, 46, 20, 0, time.UTC),
UsageLimit: 5,
UsedTimes: 1,
Valid: false,
},
},
},
}
for _, tc := range tt {
for _, user := range users {
t.Run(tc.name, func(t *testing.T) {
apiHandler, am, done := channel.BuildApiBlackBoxWithDBState(t, "../testdata/setup_keys.sql", nil, true)
req := testing_tools.BuildRequest(t, []byte{}, tc.requestType, tc.requestPath, user.userId)
recorder := httptest.NewRecorder()
apiHandler.ServeHTTP(recorder, req)
content, expectResponse := testing_tools.ReadResponse(t, recorder, tc.expectedStatus, user.expectResponse)
if !expectResponse {
return
}
got := []api.SetupKey{}
if err := json.Unmarshal(content, &got); err != nil {
t.Fatalf("Sent content is not in correct json format; %v", err)
}
sort.Slice(got, func(i, j int) bool {
return got[i].UsageLimit < got[j].UsageLimit
})
sort.Slice(tc.expectedResponse, func(i, j int) bool {
return tc.expectedResponse[i].UsageLimit < tc.expectedResponse[j].UsageLimit
})
db := testing_tools.GetDB(t, am.GetStore())
for i := range tc.expectedResponse {
gotID := got[i].Id
gotName := got[i].Name
gotRevoked := got[i].Revoked
validateCreatedKey(t, tc.expectedResponse[i], &got[i])
// Verify each setup key in DB via gorm
dbKey := testing_tools.VerifySetupKeyInDB(t, db, gotID)
assert.Equal(t, gotName, dbKey.Name)
assert.Equal(t, gotRevoked, dbKey.Revoked)
}
select {
case <-done:
case <-time.After(time.Second):
t.Error("timeout waiting for peerShouldNotReceiveUpdate")
}
})
}
}
}
func Test_SetupKeys_Delete(t *testing.T) {
users := []struct {
name string
userId string
expectResponse bool
}{
{
name: "Regular user",
userId: testing_tools.TestUserId,
expectResponse: false,
},
{
name: "Admin user",
userId: testing_tools.TestAdminId,
expectResponse: true,
},
{
name: "Owner user",
userId: testing_tools.TestOwnerId,
expectResponse: true,
},
{
name: "Regular service user",
userId: testing_tools.TestServiceUserId,
expectResponse: false,
},
{
name: "Admin service user",
userId: testing_tools.TestServiceAdminId,
expectResponse: true,
},
{
name: "Blocked user",
userId: testing_tools.BlockedUserId,
expectResponse: false,
},
{
name: "Other user",
userId: testing_tools.OtherUserId,
expectResponse: false,
},
{
name: "Invalid token",
userId: testing_tools.InvalidToken,
expectResponse: false,
},
}
tt := []struct {
name string
expectedStatus int
expectedResponse *api.SetupKey
requestType string
requestPath string
requestId string
}{
{
name: "Delete existing valid Setup Key",
requestType: http.MethodDelete,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.TestKeyId,
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: false,
State: "valid",
Type: "one-off",
UpdatedAt: time.Date(2021, time.August, 19, 20, 46, 20, 0, time.UTC),
UsageLimit: 1,
UsedTimes: 0,
Valid: true,
},
},
{
name: "Delete existing expired Setup Key",
requestType: http.MethodDelete,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.ExpiredKeyId,
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: true,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: false,
State: "expired",
Type: "reusable",
UpdatedAt: time.Date(2021, time.August, 19, 20, 46, 20, 0, time.UTC),
UsageLimit: 5,
UsedTimes: 1,
Valid: false,
},
},
{
name: "Delete existing revoked Setup Key",
requestType: http.MethodDelete,
requestPath: "/api/setup-keys/{id}",
requestId: testing_tools.RevokedKeyId,
expectedStatus: http.StatusOK,
expectedResponse: &api.SetupKey{
AutoGroups: []string{testing_tools.TestGroupId},
Ephemeral: false,
Expires: time.Time{},
Id: "",
Key: "",
LastUsed: time.Time{},
Name: testing_tools.ExistingKeyName,
Revoked: true,
State: "revoked",
Type: "reusable",
UpdatedAt: time.Date(2021, time.August, 19, 20, 46, 20, 0, time.UTC),
UsageLimit: 3,
UsedTimes: 0,
Valid: false,
},
},
{
name: "Delete non-existing Setup Key",
requestType: http.MethodDelete,
requestPath: "/api/setup-keys/{id}",
requestId: "someId",
expectedStatus: http.StatusNotFound,
expectedResponse: nil,
},
}
for _, tc := range tt {
for _, user := range users {
t.Run(tc.name, func(t *testing.T) {
apiHandler, am, done := channel.BuildApiBlackBoxWithDBState(t, "../testdata/setup_keys.sql", nil, true)
req := testing_tools.BuildRequest(t, []byte{}, tc.requestType, strings.Replace(tc.requestPath, "{id}", tc.requestId, 1), user.userId)
recorder := httptest.NewRecorder()
apiHandler.ServeHTTP(recorder, req)
content, expectResponse := testing_tools.ReadResponse(t, recorder, tc.expectedStatus, user.expectResponse)
if !expectResponse {
return
}
got := &api.SetupKey{}
if err := json.Unmarshal(content, &got); err != nil {
t.Fatalf("Sent content is not in correct json format; %v", err)
}
// Verify setup key deleted from DB via gorm
db := testing_tools.GetDB(t, am.GetStore())
testing_tools.VerifySetupKeyNotInDB(t, db, got.Id)
select {
case <-done:
case <-time.After(time.Second):
t.Error("timeout waiting for peerShouldNotReceiveUpdate")
}
})
}
}
}
func validateCreatedKey(t *testing.T, expectedKey *api.SetupKey, got *api.SetupKey) {
t.Helper()
if (got.Expires.After(time.Now().Add(-1*time.Minute)) && got.Expires.Before(time.Now().Add(testing_tools.ExpiresIn*time.Second))) ||
got.Expires.After(time.Date(2300, 01, 01, 0, 0, 0, 0, time.Local)) ||
got.Expires.Before(time.Date(1950, 01, 01, 0, 0, 0, 0, time.Local)) {
got.Expires = time.Time{}
expectedKey.Expires = time.Time{}
}
if got.Id == "" {
t.Fatalf("Expected key to have an ID")
}
got.Id = ""
if got.Key == "" {
t.Fatalf("Expected key to have a key")
}
got.Key = ""
if got.UpdatedAt.After(time.Now().Add(-1*time.Minute)) && got.UpdatedAt.Before(time.Now().Add(+1*time.Minute)) {
got.UpdatedAt = time.Time{}
expectedKey.UpdatedAt = time.Time{}
}
expectedKey.UpdatedAt = expectedKey.UpdatedAt.In(time.UTC)
got.UpdatedAt = got.UpdatedAt.In(time.UTC)
assert.Equal(t, expectedKey, got)
}