mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 03:37:34 +00:00
feat: App Keys API v2 (#10140)
# Which Problems Are Solved This PR *partially* addresses #9450 . Specifically, it implements the resource based API for app keys. This PR, together with https://github.com/zitadel/zitadel/pull/10077 completes #9450 . # How the Problems Are Solved - Implementation of the following endpoints: `CreateApplicationKey`, `DeleteApplicationKey`, `GetApplicationKey`, `ListApplicationKeys` - `ListApplicationKeys` can filter by project, app or organization ID. Sorting is also possible according to some criteria. - All endpoints use permissions V2 # TODO - [x] Deprecate old endpoints # Additional Context Closes #9450
This commit is contained in:
206
internal/api/grpc/app/v2beta/integration_test/app_key_test.go
Normal file
206
internal/api/grpc/app/v2beta/integration_test/app_key_test.go
Normal file
@@ -0,0 +1,206 @@
|
||||
//go:build integration
|
||||
|
||||
package app_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
app "github.com/zitadel/zitadel/pkg/grpc/app/v2beta"
|
||||
)
|
||||
|
||||
func TestCreateApplicationKey(t *testing.T) {
|
||||
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
||||
createdApp := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
testName string
|
||||
creationRequest *app.CreateApplicationKeyRequest
|
||||
inputCtx context.Context
|
||||
|
||||
expectedErrorType codes.Code
|
||||
}{
|
||||
{
|
||||
testName: "when app id is not found should return failed precondition",
|
||||
inputCtx: IAMOwnerCtx,
|
||||
creationRequest: &app.CreateApplicationKeyRequest{
|
||||
ProjectId: p.GetId(),
|
||||
AppId: gofakeit.UUID(),
|
||||
ExpirationDate: timestamppb.New(time.Now().AddDate(0, 0, 1).UTC()),
|
||||
},
|
||||
expectedErrorType: codes.FailedPrecondition,
|
||||
},
|
||||
{
|
||||
testName: "when CreateAPIApp request is valid should create app and return no error",
|
||||
inputCtx: IAMOwnerCtx,
|
||||
creationRequest: &app.CreateApplicationKeyRequest{
|
||||
ProjectId: p.GetId(),
|
||||
AppId: createdApp.GetAppId(),
|
||||
ExpirationDate: timestamppb.New(time.Now().AddDate(0, 0, 1).UTC()),
|
||||
},
|
||||
},
|
||||
|
||||
// LoginUser
|
||||
{
|
||||
testName: "when user has no project.app.write permission for app key generation should return permission error",
|
||||
inputCtx: LoginUserCtx,
|
||||
creationRequest: &app.CreateApplicationKeyRequest{
|
||||
ProjectId: p.GetId(),
|
||||
AppId: createdApp.GetAppId(),
|
||||
ExpirationDate: timestamppb.New(time.Now().AddDate(0, 0, 1).UTC()),
|
||||
},
|
||||
expectedErrorType: codes.PermissionDenied,
|
||||
},
|
||||
|
||||
// OrgOwner
|
||||
{
|
||||
testName: "when user is OrgOwner app key request should succeed",
|
||||
inputCtx: OrgOwnerCtx,
|
||||
creationRequest: &app.CreateApplicationKeyRequest{
|
||||
ProjectId: p.GetId(),
|
||||
AppId: createdApp.GetAppId(),
|
||||
ExpirationDate: timestamppb.New(time.Now().AddDate(0, 0, 1).UTC()),
|
||||
},
|
||||
},
|
||||
|
||||
// ProjectOwner
|
||||
{
|
||||
testName: "when user is ProjectOwner app key request should succeed",
|
||||
inputCtx: projectOwnerCtx,
|
||||
creationRequest: &app.CreateApplicationKeyRequest{
|
||||
ProjectId: p.GetId(),
|
||||
AppId: createdApp.GetAppId(),
|
||||
ExpirationDate: timestamppb.New(time.Now().AddDate(0, 0, 1).UTC()),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
res, err := instance.Client.AppV2Beta.CreateApplicationKey(tc.inputCtx, tc.creationRequest)
|
||||
|
||||
require.Equal(t, tc.expectedErrorType, status.Code(err))
|
||||
if tc.expectedErrorType == codes.OK {
|
||||
assert.NotZero(t, res.GetId())
|
||||
assert.NotZero(t, res.GetCreationDate())
|
||||
assert.NotZero(t, res.GetKeyDetails())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteApplicationKey(t *testing.T) {
|
||||
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
||||
createdApp := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
testName string
|
||||
deletionRequest func(ttt *testing.T) *app.DeleteApplicationKeyRequest
|
||||
inputCtx context.Context
|
||||
|
||||
expectedErrorType codes.Code
|
||||
}{
|
||||
{
|
||||
testName: "when app key ID is not found should return not found error",
|
||||
inputCtx: IAMOwnerCtx,
|
||||
deletionRequest: func(ttt *testing.T) *app.DeleteApplicationKeyRequest {
|
||||
return &app.DeleteApplicationKeyRequest{
|
||||
Id: gofakeit.UUID(),
|
||||
ProjectId: p.GetId(),
|
||||
ApplicationId: createdApp.GetAppId(),
|
||||
}
|
||||
},
|
||||
expectedErrorType: codes.NotFound,
|
||||
},
|
||||
{
|
||||
testName: "when valid app key ID should delete successfully",
|
||||
inputCtx: IAMOwnerCtx,
|
||||
deletionRequest: func(ttt *testing.T) *app.DeleteApplicationKeyRequest {
|
||||
createdAppKey := createAppKey(ttt, IAMOwnerCtx, instance, p.GetId(), createdApp.GetAppId(), time.Now().AddDate(0, 0, 1))
|
||||
|
||||
return &app.DeleteApplicationKeyRequest{
|
||||
Id: createdAppKey.GetId(),
|
||||
ProjectId: p.GetId(),
|
||||
ApplicationId: createdApp.GetAppId(),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// LoginUser
|
||||
{
|
||||
testName: "when user has no project.app.write permission for app key deletion should return permission error",
|
||||
inputCtx: LoginUserCtx,
|
||||
deletionRequest: func(ttt *testing.T) *app.DeleteApplicationKeyRequest {
|
||||
createdAppKey := createAppKey(ttt, IAMOwnerCtx, instance, p.GetId(), createdApp.GetAppId(), time.Now().AddDate(0, 0, 1))
|
||||
|
||||
return &app.DeleteApplicationKeyRequest{
|
||||
Id: createdAppKey.GetId(),
|
||||
ProjectId: p.GetId(),
|
||||
ApplicationId: createdApp.GetAppId(),
|
||||
}
|
||||
},
|
||||
expectedErrorType: codes.PermissionDenied,
|
||||
},
|
||||
|
||||
// ProjectOwner
|
||||
{
|
||||
testName: "when user is OrgOwner API request should succeed",
|
||||
inputCtx: projectOwnerCtx,
|
||||
deletionRequest: func(ttt *testing.T) *app.DeleteApplicationKeyRequest {
|
||||
createdAppKey := createAppKey(ttt, IAMOwnerCtx, instance, p.GetId(), createdApp.GetAppId(), time.Now().AddDate(0, 0, 1))
|
||||
|
||||
return &app.DeleteApplicationKeyRequest{
|
||||
Id: createdAppKey.GetId(),
|
||||
ProjectId: p.GetId(),
|
||||
ApplicationId: createdApp.GetAppId(),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// OrganizationOwner
|
||||
{
|
||||
testName: "when user is OrgOwner app key deletion request should succeed",
|
||||
inputCtx: OrgOwnerCtx,
|
||||
deletionRequest: func(ttt *testing.T) *app.DeleteApplicationKeyRequest {
|
||||
createdAppKey := createAppKey(ttt, IAMOwnerCtx, instance, p.GetId(), createdApp.GetAppId(), time.Now().AddDate(0, 0, 1))
|
||||
|
||||
return &app.DeleteApplicationKeyRequest{
|
||||
Id: createdAppKey.GetId(),
|
||||
ProjectId: p.GetId(),
|
||||
ApplicationId: createdApp.GetAppId(),
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given
|
||||
deletionReq := tc.deletionRequest(t)
|
||||
|
||||
// When
|
||||
res, err := instance.Client.AppV2Beta.DeleteApplicationKey(tc.inputCtx, deletionReq)
|
||||
|
||||
// Then
|
||||
require.Equal(t, tc.expectedErrorType, status.Code(err))
|
||||
if tc.expectedErrorType == codes.OK {
|
||||
assert.NotEmpty(t, res.GetDeletionDate())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
//go:build integration
|
||||
|
||||
package instance_test
|
||||
package app_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -653,9 +653,9 @@ func TestUpdateApplication_WithDifferentPermissions(t *testing.T) {
|
||||
})
|
||||
require.Nil(t, appNameChangeErr)
|
||||
|
||||
appForAPIConfigChangeForProjectOwner := createAPIApp(t, p.GetId())
|
||||
appForAPIConfigChangeForOrgOwner := createAPIApp(t, p.GetId())
|
||||
appForAPIConfigChangeForLoginUser := createAPIApp(t, p.GetId())
|
||||
appForAPIConfigChangeForProjectOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
appForAPIConfigChangeForOrgOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
appForAPIConfigChangeForLoginUser := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
|
||||
appForOIDCConfigChangeForProjectOwner := createOIDCApp(t, baseURI, p.GetId())
|
||||
appForOIDCConfigChangeForOrgOwner := createOIDCApp(t, baseURI, p.GetId())
|
||||
@@ -914,9 +914,9 @@ func TestDeleteApplication(t *testing.T) {
|
||||
func TestDeleteApplication_WithDifferentPermissions(t *testing.T) {
|
||||
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
||||
|
||||
appToDeleteForLoginUser := createAPIApp(t, p.GetId())
|
||||
appToDeleteForProjectOwner := createAPIApp(t, p.GetId())
|
||||
appToDeleteForOrgOwner := createAPIApp(t, p.GetId())
|
||||
appToDeleteForLoginUser := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
appToDeleteForProjectOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
appToDeleteForOrgOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
|
||||
t.Parallel()
|
||||
tt := []struct {
|
||||
@@ -1035,9 +1035,9 @@ func TestDeactivateApplication(t *testing.T) {
|
||||
func TestDeactivateApplication_WithDifferentPermissions(t *testing.T) {
|
||||
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
||||
|
||||
appToDeactivateForLoginUser := createAPIApp(t, p.GetId())
|
||||
appToDeactivateForPrjectOwner := createAPIApp(t, p.GetId())
|
||||
appToDeactivateForOrgOwner := createAPIApp(t, p.GetId())
|
||||
appToDeactivateForLoginUser := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
appToDeactivateForPrjectOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
appToDeactivateForOrgOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
@@ -1162,13 +1162,13 @@ func TestReactivateApplication(t *testing.T) {
|
||||
func TestReactivateApplication_WithDifferentPermissions(t *testing.T) {
|
||||
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
||||
|
||||
appToReactivateForLoginUser := createAPIApp(t, p.GetId())
|
||||
appToReactivateForLoginUser := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
deactivateApp(t, appToReactivateForLoginUser, p.GetId())
|
||||
|
||||
appToReactivateForProjectOwner := createAPIApp(t, p.GetId())
|
||||
appToReactivateForProjectOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
deactivateApp(t, appToReactivateForProjectOwner, p.GetId())
|
||||
|
||||
appToReactivateForOrgOwner := createAPIApp(t, p.GetId())
|
||||
appToReactivateForOrgOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
deactivateApp(t, appToReactivateForOrgOwner, p.GetId())
|
||||
|
||||
t.Parallel()
|
||||
@@ -1342,9 +1342,9 @@ func TestRegenerateClientSecret(t *testing.T) {
|
||||
func TestRegenerateClientSecret_WithDifferentPermissions(t *testing.T) {
|
||||
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
||||
|
||||
apiAppToRegenForLoginUser := createAPIApp(t, p.GetId())
|
||||
apiAppToRegenForProjectOwner := createAPIApp(t, p.GetId())
|
||||
apiAppToRegenForOrgOwner := createAPIApp(t, p.GetId())
|
||||
apiAppToRegenForLoginUser := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
apiAppToRegenForProjectOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
apiAppToRegenForOrgOwner := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
|
||||
oidcAppToRegenForLoginUser := createOIDCApp(t, baseURI, p.GetId())
|
||||
oidcAppToRegenForProjectOwner := createOIDCApp(t, baseURI, p.GetId())
|
||||
|
@@ -1,6 +1,6 @@
|
||||
//go:build integration
|
||||
|
||||
package instance_test
|
||||
package app_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -165,9 +165,9 @@ func TestListApplications(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
createdApiApp, apiAppName := createAPIAppWithName(t, p.GetId())
|
||||
createdApiApp, apiAppName := createAPIAppWithName(t, IAMOwnerCtx, instance, p.GetId())
|
||||
|
||||
createdDeactivatedApiApp, deactivatedApiAppName := createAPIAppWithName(t, p.GetId())
|
||||
createdDeactivatedApiApp, deactivatedApiAppName := createAPIAppWithName(t, IAMOwnerCtx, instance, p.GetId())
|
||||
deactivateApp(t, createdDeactivatedApiApp, p.GetId())
|
||||
|
||||
_, createdSAMLApp, samlAppName := createSAMLAppWithName(t, gofakeit.URL(), p.GetId())
|
||||
@@ -573,3 +573,248 @@ func TestListApplications_WithPermissionV2(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetApplicationKey(t *testing.T) {
|
||||
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
||||
createdApiApp := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
createdAppKey := createAppKey(t, IAMOwnerCtx, instance, p.GetId(), createdApiApp.GetAppId(), time.Now().AddDate(0, 0, 1))
|
||||
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
testName string
|
||||
inputRequest *app.GetApplicationKeyRequest
|
||||
inputCtx context.Context
|
||||
|
||||
expectedErrorType codes.Code
|
||||
expectedAppKeyID string
|
||||
}{
|
||||
{
|
||||
testName: "when unknown app ID should return not found error",
|
||||
inputCtx: IAMOwnerCtx,
|
||||
inputRequest: &app.GetApplicationKeyRequest{
|
||||
Id: gofakeit.Sentence(2),
|
||||
},
|
||||
|
||||
expectedErrorType: codes.NotFound,
|
||||
},
|
||||
{
|
||||
testName: "when user has no permission should return membership not found error",
|
||||
inputCtx: NoPermissionCtx,
|
||||
inputRequest: &app.GetApplicationKeyRequest{
|
||||
Id: createdAppKey.GetId(),
|
||||
},
|
||||
|
||||
expectedErrorType: codes.NotFound,
|
||||
},
|
||||
{
|
||||
testName: "when providing API app ID should return valid API app result",
|
||||
inputCtx: projectOwnerCtx,
|
||||
inputRequest: &app.GetApplicationKeyRequest{
|
||||
Id: createdAppKey.GetId(),
|
||||
},
|
||||
|
||||
expectedAppKeyID: createdAppKey.GetId(),
|
||||
},
|
||||
{
|
||||
testName: "when user is OrgOwner should return request key",
|
||||
inputCtx: OrgOwnerCtx,
|
||||
inputRequest: &app.GetApplicationKeyRequest{
|
||||
Id: createdAppKey.GetId(),
|
||||
ProjectId: p.GetId(),
|
||||
},
|
||||
|
||||
expectedAppKeyID: createdAppKey.GetId(),
|
||||
},
|
||||
{
|
||||
testName: "when user is IAMOwner should return request key",
|
||||
inputCtx: OrgOwnerCtx,
|
||||
inputRequest: &app.GetApplicationKeyRequest{
|
||||
Id: createdAppKey.GetId(),
|
||||
OrganizationId: instance.DefaultOrg.GetId(),
|
||||
},
|
||||
|
||||
expectedAppKeyID: createdAppKey.GetId(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputCtx, 30*time.Second)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
// When
|
||||
res, err := instance.Client.AppV2Beta.GetApplicationKey(tc.inputCtx, tc.inputRequest)
|
||||
|
||||
// Then
|
||||
require.Equal(t, tc.expectedErrorType, status.Code(err))
|
||||
if tc.expectedErrorType == codes.OK {
|
||||
|
||||
assert.Equal(t, tc.expectedAppKeyID, res.GetId())
|
||||
assert.NotEmpty(t, res.GetCreationDate())
|
||||
assert.NotEmpty(t, res.GetExpirationDate())
|
||||
}
|
||||
}, retryDuration, tick)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListApplicationKeys(t *testing.T) {
|
||||
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
||||
|
||||
createdApiApp1 := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
createdApiApp2 := createAPIApp(t, IAMOwnerCtx, instance, p.GetId())
|
||||
|
||||
tomorrow := time.Now().AddDate(0, 0, 1)
|
||||
in2Days := tomorrow.AddDate(0, 0, 1)
|
||||
in3Days := in2Days.AddDate(0, 0, 1)
|
||||
|
||||
appKey1 := createAppKey(t, IAMOwnerCtx, instance, p.GetId(), createdApiApp1.GetAppId(), in2Days)
|
||||
appKey2 := createAppKey(t, IAMOwnerCtx, instance, p.GetId(), createdApiApp1.GetAppId(), in3Days)
|
||||
appKey3 := createAppKey(t, IAMOwnerCtx, instance, p.GetId(), createdApiApp1.GetAppId(), tomorrow)
|
||||
appKey4 := createAppKey(t, IAMOwnerCtx, instance, p.GetId(), createdApiApp2.GetAppId(), tomorrow)
|
||||
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
testName string
|
||||
inputRequest *app.ListApplicationKeysRequest
|
||||
deps func() (projectID, applicationID, organizationID string)
|
||||
inputCtx context.Context
|
||||
|
||||
expectedErrorType codes.Code
|
||||
expectedAppKeysIDs []string
|
||||
}{
|
||||
{
|
||||
testName: "when sorting by expiration date should return keys sorted by expiration date ascending",
|
||||
inputCtx: LoginUserCtx,
|
||||
inputRequest: &app.ListApplicationKeysRequest{
|
||||
ResourceId: &app.ListApplicationKeysRequest_ProjectId{ProjectId: p.GetId()},
|
||||
Pagination: &filter.PaginationRequest{Asc: true},
|
||||
SortingColumn: app.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_EXPIRATION,
|
||||
},
|
||||
expectedAppKeysIDs: []string{appKey3.GetId(), appKey4.GetId(), appKey1.GetId(), appKey2.GetId()},
|
||||
},
|
||||
{
|
||||
testName: "when sorting by creation date should return keys sorted by creation date descending",
|
||||
inputCtx: IAMOwnerCtx,
|
||||
inputRequest: &app.ListApplicationKeysRequest{
|
||||
ResourceId: &app.ListApplicationKeysRequest_ProjectId{ProjectId: p.GetId()},
|
||||
SortingColumn: app.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_CREATION_DATE,
|
||||
},
|
||||
expectedAppKeysIDs: []string{appKey4.GetId(), appKey3.GetId(), appKey2.GetId(), appKey1.GetId()},
|
||||
},
|
||||
{
|
||||
testName: "when filtering by app ID should return keys matching app ID sorted by ID",
|
||||
inputCtx: projectOwnerCtx,
|
||||
inputRequest: &app.ListApplicationKeysRequest{
|
||||
Pagination: &filter.PaginationRequest{Asc: true},
|
||||
ResourceId: &app.ListApplicationKeysRequest_ApplicationId{ApplicationId: createdApiApp1.GetAppId()},
|
||||
},
|
||||
expectedAppKeysIDs: []string{appKey1.GetId(), appKey2.GetId(), appKey3.GetId()},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputCtx, 5*time.Second)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
// When
|
||||
res, err := instance.Client.AppV2Beta.ListApplicationKeys(tc.inputCtx, tc.inputRequest)
|
||||
|
||||
// Then
|
||||
require.Equal(ttt, tc.expectedErrorType, status.Code(err))
|
||||
if tc.expectedErrorType == codes.OK {
|
||||
require.Len(ttt, res.GetKeys(), len(tc.expectedAppKeysIDs))
|
||||
|
||||
for i, k := range res.GetKeys() {
|
||||
assert.Equal(ttt, tc.expectedAppKeysIDs[i], k.GetId())
|
||||
}
|
||||
}
|
||||
}, retryDuration, tick)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListApplicationKeys_WithPermissionV2(t *testing.T) {
|
||||
ensureFeaturePermissionV2Enabled(t, instancePermissionV2)
|
||||
iamOwnerCtx := instancePermissionV2.WithAuthorization(context.Background(), integration.UserTypeIAMOwner)
|
||||
loginUserCtx := instancePermissionV2.WithAuthorization(context.Background(), integration.UserTypeLogin)
|
||||
p, projectOwnerCtx := getProjectAndProjectContext(t, instancePermissionV2, iamOwnerCtx)
|
||||
|
||||
createdApiApp1 := createAPIApp(t, iamOwnerCtx, instancePermissionV2, p.GetId())
|
||||
createdApiApp2 := createAPIApp(t, iamOwnerCtx, instancePermissionV2, p.GetId())
|
||||
|
||||
tomorrow := time.Now().AddDate(0, 0, 1)
|
||||
in2Days := tomorrow.AddDate(0, 0, 1)
|
||||
in3Days := in2Days.AddDate(0, 0, 1)
|
||||
|
||||
appKey1 := createAppKey(t, iamOwnerCtx, instancePermissionV2, p.GetId(), createdApiApp1.GetAppId(), in2Days)
|
||||
appKey2 := createAppKey(t, iamOwnerCtx, instancePermissionV2, p.GetId(), createdApiApp1.GetAppId(), in3Days)
|
||||
appKey3 := createAppKey(t, iamOwnerCtx, instancePermissionV2, p.GetId(), createdApiApp1.GetAppId(), tomorrow)
|
||||
appKey4 := createAppKey(t, iamOwnerCtx, instancePermissionV2, p.GetId(), createdApiApp2.GetAppId(), tomorrow)
|
||||
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
testName string
|
||||
inputRequest *app.ListApplicationKeysRequest
|
||||
deps func() (projectID, applicationID, organizationID string)
|
||||
inputCtx context.Context
|
||||
|
||||
expectedErrorType codes.Code
|
||||
expectedAppKeysIDs []string
|
||||
}{
|
||||
{
|
||||
testName: "when sorting by expiration date should return keys sorted by expiration date ascending",
|
||||
inputCtx: loginUserCtx,
|
||||
inputRequest: &app.ListApplicationKeysRequest{
|
||||
Pagination: &filter.PaginationRequest{Asc: true},
|
||||
SortingColumn: app.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_EXPIRATION,
|
||||
},
|
||||
expectedAppKeysIDs: []string{appKey3.GetId(), appKey4.GetId(), appKey1.GetId(), appKey2.GetId()},
|
||||
},
|
||||
{
|
||||
testName: "when sorting by creation date should return keys sorted by creation date descending",
|
||||
inputCtx: iamOwnerCtx,
|
||||
inputRequest: &app.ListApplicationKeysRequest{
|
||||
SortingColumn: app.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_CREATION_DATE,
|
||||
},
|
||||
expectedAppKeysIDs: []string{appKey4.GetId(), appKey3.GetId(), appKey2.GetId(), appKey1.GetId()},
|
||||
},
|
||||
{
|
||||
testName: "when filtering by app ID should return keys matching app ID sorted by ID",
|
||||
inputCtx: projectOwnerCtx,
|
||||
inputRequest: &app.ListApplicationKeysRequest{
|
||||
Pagination: &filter.PaginationRequest{Asc: true},
|
||||
ResourceId: &app.ListApplicationKeysRequest_ApplicationId{ApplicationId: createdApiApp1.GetAppId()},
|
||||
},
|
||||
expectedAppKeysIDs: []string{appKey1.GetId(), appKey2.GetId(), appKey3.GetId()},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
// t.Parallel()
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputCtx, 5*time.Second)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
// When
|
||||
res, err := instancePermissionV2.Client.AppV2Beta.ListApplicationKeys(tc.inputCtx, tc.inputRequest)
|
||||
|
||||
// Then
|
||||
require.Equal(ttt, tc.expectedErrorType, status.Code(err))
|
||||
if tc.expectedErrorType == codes.OK {
|
||||
require.Len(ttt, res.GetKeys(), len(tc.expectedAppKeysIDs))
|
||||
|
||||
for i, k := range res.GetKeys() {
|
||||
assert.Equal(ttt, tc.expectedAppKeysIDs[i], k.GetId())
|
||||
}
|
||||
}
|
||||
}, retryDuration, tick)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
//go:build integration
|
||||
|
||||
package instance_test
|
||||
package app_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
app "github.com/zitadel/zitadel/pkg/grpc/app/v2beta"
|
||||
@@ -150,14 +151,14 @@ func createOIDCApp(t *testing.T, baseURI, projctID string) *app.CreateApplicatio
|
||||
return app
|
||||
}
|
||||
|
||||
func createAPIAppWithName(t *testing.T, projectID string) (*app.CreateApplicationResponse, string) {
|
||||
func createAPIAppWithName(t *testing.T, ctx context.Context, inst *integration.Instance, projectID string) (*app.CreateApplicationResponse, string) {
|
||||
appName := gofakeit.AppName()
|
||||
|
||||
reqForAPIAppCreation := &app.CreateApplicationRequest_ApiRequest{
|
||||
ApiRequest: &app.CreateAPIApplicationRequest{AuthMethodType: app.APIAuthMethodType_API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT},
|
||||
}
|
||||
|
||||
appForAPIConfigChange, appAPIConfigChangeErr := instance.Client.AppV2Beta.CreateApplication(IAMOwnerCtx, &app.CreateApplicationRequest{
|
||||
appForAPIConfigChange, appAPIConfigChangeErr := inst.Client.AppV2Beta.CreateApplication(ctx, &app.CreateApplicationRequest{
|
||||
ProjectId: projectID,
|
||||
Name: appName,
|
||||
CreationRequestType: reqForAPIAppCreation,
|
||||
@@ -167,8 +168,8 @@ func createAPIAppWithName(t *testing.T, projectID string) (*app.CreateApplicatio
|
||||
return appForAPIConfigChange, appName
|
||||
}
|
||||
|
||||
func createAPIApp(t *testing.T, projectID string) *app.CreateApplicationResponse {
|
||||
res, _ := createAPIAppWithName(t, projectID)
|
||||
func createAPIApp(t *testing.T, ctx context.Context, inst *integration.Instance, projectID string) *app.CreateApplicationResponse {
|
||||
res, _ := createAPIAppWithName(t, ctx, inst, projectID)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -203,3 +204,17 @@ func ensureFeaturePermissionV2Enabled(t *testing.T, instance *integration.Instan
|
||||
assert.True(tt, f.PermissionCheckV2.GetEnabled())
|
||||
}, retryDuration, tick, "timed out waiting for ensuring instance feature")
|
||||
}
|
||||
|
||||
func createAppKey(t *testing.T, ctx context.Context, inst *integration.Instance, projectID, appID string, expirationDate time.Time) *app.CreateApplicationKeyResponse {
|
||||
res, err := inst.Client.AppV2Beta.CreateApplicationKey(ctx,
|
||||
&app.CreateApplicationKeyRequest{
|
||||
AppId: appID,
|
||||
ProjectId: projectID,
|
||||
ExpirationDate: timestamppb.New(expirationDate.UTC()),
|
||||
},
|
||||
)
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
return res
|
||||
}
|
||||
|
Reference in New Issue
Block a user