mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-15 20:38:36 +00:00

# 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
821 lines
26 KiB
Go
821 lines
26 KiB
Go
//go:build integration
|
|
|
|
package app_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
"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"
|
|
|
|
"github.com/zitadel/zitadel/internal/integration"
|
|
app "github.com/zitadel/zitadel/pkg/grpc/app/v2beta"
|
|
"github.com/zitadel/zitadel/pkg/grpc/filter/v2"
|
|
)
|
|
|
|
func TestGetApplication(t *testing.T) {
|
|
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
|
|
|
apiAppName := gofakeit.AppName()
|
|
createdApiApp, errAPIAppCreation := instance.Client.AppV2Beta.CreateApplication(IAMOwnerCtx, &app.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: apiAppName,
|
|
CreationRequestType: &app.CreateApplicationRequest_ApiRequest{
|
|
ApiRequest: &app.CreateAPIApplicationRequest{
|
|
AuthMethodType: app.APIAuthMethodType_API_AUTH_METHOD_TYPE_BASIC,
|
|
},
|
|
},
|
|
})
|
|
require.Nil(t, errAPIAppCreation)
|
|
|
|
samlAppName := gofakeit.AppName()
|
|
createdSAMLApp, errSAMLAppCreation := instance.Client.AppV2Beta.CreateApplication(IAMOwnerCtx, &app.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: samlAppName,
|
|
CreationRequestType: &app.CreateApplicationRequest_SamlRequest{
|
|
SamlRequest: &app.CreateSAMLApplicationRequest{
|
|
LoginVersion: &app.LoginVersion{Version: &app.LoginVersion_LoginV1{LoginV1: &app.LoginV1{}}},
|
|
Metadata: &app.CreateSAMLApplicationRequest_MetadataXml{MetadataXml: samlMetadataGen(gofakeit.URL())},
|
|
},
|
|
},
|
|
})
|
|
require.Nil(t, errSAMLAppCreation)
|
|
|
|
oidcAppName := gofakeit.AppName()
|
|
createdOIDCApp, errOIDCAppCreation := instance.Client.AppV2Beta.CreateApplication(IAMOwnerCtx, &app.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: oidcAppName,
|
|
CreationRequestType: &app.CreateApplicationRequest_OidcRequest{
|
|
OidcRequest: &app.CreateOIDCApplicationRequest{
|
|
RedirectUris: []string{"http://example.com"},
|
|
ResponseTypes: []app.OIDCResponseType{app.OIDCResponseType_OIDC_RESPONSE_TYPE_CODE},
|
|
GrantTypes: []app.OIDCGrantType{app.OIDCGrantType_OIDC_GRANT_TYPE_AUTHORIZATION_CODE},
|
|
AppType: app.OIDCAppType_OIDC_APP_TYPE_WEB,
|
|
AuthMethodType: app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_BASIC,
|
|
PostLogoutRedirectUris: []string{"http://example.com/home"},
|
|
Version: app.OIDCVersion_OIDC_VERSION_1_0,
|
|
AccessTokenType: app.OIDCTokenType_OIDC_TOKEN_TYPE_JWT,
|
|
BackChannelLogoutUri: "http://example.com/logout",
|
|
LoginVersion: &app.LoginVersion{Version: &app.LoginVersion_LoginV2{LoginV2: &app.LoginV2{BaseUri: &baseURI}}},
|
|
},
|
|
},
|
|
})
|
|
require.Nil(t, errOIDCAppCreation)
|
|
|
|
t.Parallel()
|
|
|
|
tt := []struct {
|
|
testName string
|
|
inputRequest *app.GetApplicationRequest
|
|
inputCtx context.Context
|
|
|
|
expectedErrorType codes.Code
|
|
expectedAppName string
|
|
expectedAppID string
|
|
expectedApplicationType string
|
|
}{
|
|
{
|
|
testName: "when unknown app ID should return not found error",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &app.GetApplicationRequest{
|
|
Id: gofakeit.Sentence(2),
|
|
},
|
|
|
|
expectedErrorType: codes.NotFound,
|
|
},
|
|
{
|
|
testName: "when user has no permission should return membership not found error",
|
|
inputCtx: NoPermissionCtx,
|
|
inputRequest: &app.GetApplicationRequest{
|
|
Id: createdApiApp.GetAppId(),
|
|
},
|
|
|
|
expectedErrorType: codes.NotFound,
|
|
},
|
|
{
|
|
testName: "when providing API app ID should return valid API app result",
|
|
inputCtx: projectOwnerCtx,
|
|
inputRequest: &app.GetApplicationRequest{
|
|
Id: createdApiApp.GetAppId(),
|
|
},
|
|
|
|
expectedAppName: apiAppName,
|
|
expectedAppID: createdApiApp.GetAppId(),
|
|
expectedApplicationType: fmt.Sprintf("%T", &app.Application_ApiConfig{}),
|
|
},
|
|
{
|
|
testName: "when providing SAML app ID should return valid SAML app result",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &app.GetApplicationRequest{
|
|
Id: createdSAMLApp.GetAppId(),
|
|
},
|
|
|
|
expectedAppName: samlAppName,
|
|
expectedAppID: createdSAMLApp.GetAppId(),
|
|
expectedApplicationType: fmt.Sprintf("%T", &app.Application_SamlConfig{}),
|
|
},
|
|
{
|
|
testName: "when providing OIDC app ID should return valid OIDC app result",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &app.GetApplicationRequest{
|
|
Id: createdOIDCApp.GetAppId(),
|
|
},
|
|
|
|
expectedAppName: oidcAppName,
|
|
expectedAppID: createdOIDCApp.GetAppId(),
|
|
expectedApplicationType: fmt.Sprintf("%T", &app.Application_OidcConfig{}),
|
|
},
|
|
}
|
|
|
|
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.GetApplication(tc.inputCtx, tc.inputRequest)
|
|
|
|
// Then
|
|
require.Equal(t, tc.expectedErrorType, status.Code(err))
|
|
if tc.expectedErrorType == codes.OK {
|
|
|
|
assert.Equal(t, tc.expectedAppID, res.GetApp().GetId())
|
|
assert.Equal(t, tc.expectedAppName, res.GetApp().GetName())
|
|
assert.NotZero(t, res.GetApp().GetCreationDate())
|
|
assert.NotZero(t, res.GetApp().GetChangeDate())
|
|
|
|
appType := fmt.Sprintf("%T", res.GetApp().GetConfig())
|
|
assert.Equal(t, tc.expectedApplicationType, appType)
|
|
}
|
|
}, retryDuration, tick)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListApplications(t *testing.T) {
|
|
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
|
|
|
t.Parallel()
|
|
|
|
createdApiApp, apiAppName := createAPIAppWithName(t, IAMOwnerCtx, instance, p.GetId())
|
|
|
|
createdDeactivatedApiApp, deactivatedApiAppName := createAPIAppWithName(t, IAMOwnerCtx, instance, p.GetId())
|
|
deactivateApp(t, createdDeactivatedApiApp, p.GetId())
|
|
|
|
_, createdSAMLApp, samlAppName := createSAMLAppWithName(t, gofakeit.URL(), p.GetId())
|
|
|
|
createdOIDCApp, oidcAppName := createOIDCAppWithName(t, gofakeit.URL(), p.GetId())
|
|
|
|
type appWithName struct {
|
|
app *app.CreateApplicationResponse
|
|
name string
|
|
}
|
|
|
|
// Sorting
|
|
appsSortedByName := []appWithName{
|
|
{name: apiAppName, app: createdApiApp},
|
|
{name: deactivatedApiAppName, app: createdDeactivatedApiApp},
|
|
{name: samlAppName, app: createdSAMLApp},
|
|
{name: oidcAppName, app: createdOIDCApp},
|
|
}
|
|
slices.SortFunc(appsSortedByName, func(a, b appWithName) int {
|
|
if a.name < b.name {
|
|
return -1
|
|
}
|
|
if a.name > b.name {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
})
|
|
|
|
appsSortedByID := []appWithName{
|
|
{name: apiAppName, app: createdApiApp},
|
|
{name: deactivatedApiAppName, app: createdDeactivatedApiApp},
|
|
{name: samlAppName, app: createdSAMLApp},
|
|
{name: oidcAppName, app: createdOIDCApp},
|
|
}
|
|
slices.SortFunc(appsSortedByID, func(a, b appWithName) int {
|
|
if a.app.GetAppId() < b.app.GetAppId() {
|
|
return -1
|
|
}
|
|
if a.app.GetAppId() > b.app.GetAppId() {
|
|
return 1
|
|
}
|
|
return 0
|
|
})
|
|
|
|
appsSortedByCreationDate := []appWithName{
|
|
{name: apiAppName, app: createdApiApp},
|
|
{name: deactivatedApiAppName, app: createdDeactivatedApiApp},
|
|
{name: samlAppName, app: createdSAMLApp},
|
|
{name: oidcAppName, app: createdOIDCApp},
|
|
}
|
|
slices.SortFunc(appsSortedByCreationDate, func(a, b appWithName) int {
|
|
aCreationDate := a.app.GetCreationDate().AsTime()
|
|
bCreationDate := b.app.GetCreationDate().AsTime()
|
|
|
|
if aCreationDate.Before(bCreationDate) {
|
|
return -1
|
|
}
|
|
if bCreationDate.Before(aCreationDate) {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
})
|
|
|
|
tt := []struct {
|
|
testName string
|
|
inputRequest *app.ListApplicationsRequest
|
|
inputCtx context.Context
|
|
|
|
expectedOrderedList []appWithName
|
|
expectedOrderedKeys func(keys []appWithName) any
|
|
actualOrderedKeys func(keys []*app.Application) any
|
|
}{
|
|
{
|
|
testName: "when no apps found should return empty list",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: "another-id",
|
|
},
|
|
|
|
expectedOrderedList: []appWithName{},
|
|
expectedOrderedKeys: func(keys []appWithName) any { return keys },
|
|
actualOrderedKeys: func(keys []*app.Application) any { return keys },
|
|
},
|
|
{
|
|
testName: "when user has no read permission should return empty set",
|
|
inputCtx: NoPermissionCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
},
|
|
|
|
expectedOrderedList: []appWithName{},
|
|
expectedOrderedKeys: func(keys []appWithName) any { return keys },
|
|
actualOrderedKeys: func(keys []*app.Application) any { return keys },
|
|
},
|
|
{
|
|
testName: "when sorting by name should return apps sorted by name in descending order",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
SortingColumn: app.AppSorting_APP_SORT_BY_NAME,
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
},
|
|
|
|
expectedOrderedList: appsSortedByName,
|
|
expectedOrderedKeys: func(apps []appWithName) any {
|
|
names := make([]string, len(apps))
|
|
for i, a := range apps {
|
|
names[i] = a.name
|
|
}
|
|
|
|
return names
|
|
},
|
|
actualOrderedKeys: func(apps []*app.Application) any {
|
|
names := make([]string, len(apps))
|
|
for i, a := range apps {
|
|
names[i] = a.GetName()
|
|
}
|
|
|
|
return names
|
|
},
|
|
},
|
|
|
|
{
|
|
testName: "when user is project owner should return apps sorted by name in ascending order",
|
|
inputCtx: projectOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
SortingColumn: app.AppSorting_APP_SORT_BY_NAME,
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
},
|
|
|
|
expectedOrderedList: appsSortedByName,
|
|
expectedOrderedKeys: func(apps []appWithName) any {
|
|
names := make([]string, len(apps))
|
|
for i, a := range apps {
|
|
names[i] = a.name
|
|
}
|
|
|
|
return names
|
|
},
|
|
actualOrderedKeys: func(apps []*app.Application) any {
|
|
names := make([]string, len(apps))
|
|
for i, a := range apps {
|
|
names[i] = a.GetName()
|
|
}
|
|
|
|
return names
|
|
},
|
|
},
|
|
|
|
{
|
|
testName: "when sorting by id should return apps sorted by id in descending order",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
SortingColumn: app.AppSorting_APP_SORT_BY_ID,
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
},
|
|
expectedOrderedList: appsSortedByID,
|
|
expectedOrderedKeys: func(apps []appWithName) any {
|
|
ids := make([]string, len(apps))
|
|
for i, a := range apps {
|
|
ids[i] = a.app.GetAppId()
|
|
}
|
|
|
|
return ids
|
|
},
|
|
actualOrderedKeys: func(apps []*app.Application) any {
|
|
ids := make([]string, len(apps))
|
|
for i, a := range apps {
|
|
ids[i] = a.GetId()
|
|
}
|
|
|
|
return ids
|
|
},
|
|
},
|
|
{
|
|
testName: "when sorting by creation date should return apps sorted by creation date in descending order",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
SortingColumn: app.AppSorting_APP_SORT_BY_CREATION_DATE,
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
},
|
|
expectedOrderedList: appsSortedByCreationDate,
|
|
expectedOrderedKeys: func(apps []appWithName) any {
|
|
creationDates := make([]time.Time, len(apps))
|
|
for i, a := range apps {
|
|
creationDates[i] = a.app.GetCreationDate().AsTime()
|
|
}
|
|
|
|
return creationDates
|
|
},
|
|
actualOrderedKeys: func(apps []*app.Application) any {
|
|
creationDates := make([]time.Time, len(apps))
|
|
for i, a := range apps {
|
|
creationDates[i] = a.GetCreationDate().AsTime()
|
|
}
|
|
|
|
return creationDates
|
|
},
|
|
},
|
|
{
|
|
testName: "when filtering by active apps should return active apps only",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
Filters: []*app.ApplicationSearchFilter{
|
|
{Filter: &app.ApplicationSearchFilter_StateFilter{StateFilter: app.AppState_APP_STATE_ACTIVE}},
|
|
},
|
|
},
|
|
expectedOrderedList: slices.DeleteFunc(
|
|
slices.Clone(appsSortedByID),
|
|
func(a appWithName) bool { return a.name == deactivatedApiAppName },
|
|
),
|
|
expectedOrderedKeys: func(apps []appWithName) any {
|
|
creationDates := make([]time.Time, len(apps))
|
|
for i, a := range apps {
|
|
creationDates[i] = a.app.GetCreationDate().AsTime()
|
|
}
|
|
|
|
return creationDates
|
|
},
|
|
actualOrderedKeys: func(apps []*app.Application) any {
|
|
creationDates := make([]time.Time, len(apps))
|
|
for i, a := range apps {
|
|
creationDates[i] = a.GetCreationDate().AsTime()
|
|
}
|
|
|
|
return creationDates
|
|
},
|
|
},
|
|
{
|
|
testName: "when filtering by app type should return apps of matching type only",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
Filters: []*app.ApplicationSearchFilter{
|
|
{Filter: &app.ApplicationSearchFilter_OidcAppOnly{}},
|
|
},
|
|
},
|
|
expectedOrderedList: slices.DeleteFunc(
|
|
slices.Clone(appsSortedByID),
|
|
func(a appWithName) bool { return a.name != oidcAppName },
|
|
),
|
|
expectedOrderedKeys: func(apps []appWithName) any {
|
|
creationDates := make([]time.Time, len(apps))
|
|
for i, a := range apps {
|
|
creationDates[i] = a.app.GetCreationDate().AsTime()
|
|
}
|
|
|
|
return creationDates
|
|
},
|
|
actualOrderedKeys: func(apps []*app.Application) any {
|
|
creationDates := make([]time.Time, len(apps))
|
|
for i, a := range apps {
|
|
creationDates[i] = a.GetCreationDate().AsTime()
|
|
}
|
|
|
|
return creationDates
|
|
},
|
|
},
|
|
}
|
|
|
|
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.ListApplications(tc.inputCtx, tc.inputRequest)
|
|
|
|
// Then
|
|
require.Equal(ttt, codes.OK, status.Code(err))
|
|
|
|
if err == nil {
|
|
assert.Len(ttt, res.GetApplications(), len(tc.expectedOrderedList))
|
|
actualOrderedKeys := tc.actualOrderedKeys(res.GetApplications())
|
|
expectedOrderedKeys := tc.expectedOrderedKeys(tc.expectedOrderedList)
|
|
assert.ElementsMatch(ttt, expectedOrderedKeys, actualOrderedKeys)
|
|
}
|
|
}, retryDuration, tick)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListApplications_WithPermissionV2(t *testing.T) {
|
|
ensureFeaturePermissionV2Enabled(t, instancePermissionV2)
|
|
iamOwnerCtx := instancePermissionV2.WithAuthorization(context.Background(), integration.UserTypeIAMOwner)
|
|
p, projectOwnerCtx := getProjectAndProjectContext(t, instancePermissionV2, iamOwnerCtx)
|
|
_, otherProjectOwnerCtx := getProjectAndProjectContext(t, instancePermissionV2, iamOwnerCtx)
|
|
|
|
appName1, appName2, appName3 := gofakeit.AppName(), gofakeit.AppName(), gofakeit.AppName()
|
|
reqForAPIAppCreation := &app.CreateApplicationRequest_ApiRequest{
|
|
ApiRequest: &app.CreateAPIApplicationRequest{AuthMethodType: app.APIAuthMethodType_API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT},
|
|
}
|
|
|
|
app1, appAPIConfigChangeErr := instancePermissionV2.Client.AppV2Beta.CreateApplication(iamOwnerCtx, &app.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: appName1,
|
|
CreationRequestType: reqForAPIAppCreation,
|
|
})
|
|
require.Nil(t, appAPIConfigChangeErr)
|
|
|
|
app2, appAPIConfigChangeErr := instancePermissionV2.Client.AppV2Beta.CreateApplication(iamOwnerCtx, &app.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: appName2,
|
|
CreationRequestType: reqForAPIAppCreation,
|
|
})
|
|
require.Nil(t, appAPIConfigChangeErr)
|
|
|
|
app3, appAPIConfigChangeErr := instancePermissionV2.Client.AppV2Beta.CreateApplication(iamOwnerCtx, &app.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: appName3,
|
|
CreationRequestType: reqForAPIAppCreation,
|
|
})
|
|
require.Nil(t, appAPIConfigChangeErr)
|
|
|
|
t.Parallel()
|
|
|
|
tt := []struct {
|
|
testName string
|
|
inputRequest *app.ListApplicationsRequest
|
|
inputCtx context.Context
|
|
|
|
expectedCode codes.Code
|
|
expectedAppIDs []string
|
|
}{
|
|
{
|
|
testName: "when user has no read permission should return empty set",
|
|
inputCtx: instancePermissionV2.WithAuthorization(context.Background(), integration.UserTypeNoPermission),
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
},
|
|
|
|
expectedAppIDs: []string{},
|
|
},
|
|
{
|
|
testName: "when projectOwner should return full app list",
|
|
inputCtx: projectOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
},
|
|
|
|
expectedCode: codes.OK,
|
|
expectedAppIDs: []string{app1.GetAppId(), app2.GetAppId(), app3.GetAppId()},
|
|
},
|
|
{
|
|
testName: "when orgOwner should return full app list",
|
|
inputCtx: instancePermissionV2.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
},
|
|
|
|
expectedAppIDs: []string{app1.GetAppId(), app2.GetAppId(), app3.GetAppId()},
|
|
},
|
|
{
|
|
testName: "when iamOwner user should return full app list",
|
|
inputCtx: iamOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
},
|
|
|
|
expectedAppIDs: []string{app1.GetAppId(), app2.GetAppId(), app3.GetAppId()},
|
|
},
|
|
{
|
|
testName: "when other projectOwner user should return empty list",
|
|
inputCtx: otherProjectOwnerCtx,
|
|
inputRequest: &app.ListApplicationsRequest{
|
|
ProjectId: p.GetId(),
|
|
},
|
|
|
|
expectedAppIDs: []string{},
|
|
},
|
|
}
|
|
|
|
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.ListApplications(tc.inputCtx, tc.inputRequest)
|
|
|
|
// Then
|
|
require.Equal(ttt, tc.expectedCode, status.Code(err))
|
|
|
|
if err == nil {
|
|
require.Len(ttt, res.GetApplications(), len(tc.expectedAppIDs))
|
|
|
|
resAppIDs := []string{}
|
|
for _, a := range res.GetApplications() {
|
|
resAppIDs = append(resAppIDs, a.GetId())
|
|
}
|
|
|
|
assert.ElementsMatch(ttt, tc.expectedAppIDs, resAppIDs)
|
|
}
|
|
}, retryDuration, tick)
|
|
})
|
|
}
|
|
}
|
|
|
|
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)
|
|
})
|
|
}
|
|
}
|