mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-02 14:12:27 +00:00
# Which Problems Are Solved As part of our efforts to simplify the structure and versions of our APIs, were moving all existing v2beta endpoints to v2 and deprecate them. They will be removed in Zitadel V5. # How the Problems Are Solved - This PR moves app v2beta service and its endpoints to a corresponding to application v2 version. The v2beta service and endpoints are deprecated. - The comments and have been improved and, where not already done, moved from swagger annotations to proto. - All required fields have been marked with (google.api.field_behavior) = REQUIRED and validation rules have been added where missing. - Name ID of the application always `application_id`, previously was also `id` and `app_id`. - Get rid of all `app` abbreviations and name it `application` including the service name, `AppState` -> `ApplicationState` and `AppSorting` -> `ApplicationSorting` - Updated `CreateApplicationRequest`: - renamed `creation_request_type` to `application_type` and all its options to `XY_configuration` instead of `XY_request` - `RegenerateClientSecret` - renamed method to `GenerateClientSecret` - removed `app_type` from request - `ListApplicationRequest`: - removed required `project_id` and provided it as a filter - Type `ApplicationNameQuery` has been renamed to `ApplicationNameFilter` as its usage in the request - Renamed all fields and types from `config` to `configuration` - Updated `DeleteApplicationKeyRequest` - removed `organization_id` - Updated `GetApplicationKeyRequest`: - removed `project_id`, `application_id` and `organization_id`` - Updated `ListApplicationKeysRequest`: - removed oneOf `resource_id` and moved the options into filters - Name ID of the application key always `key_id`. - removed unnecessary package prefixed (`zitadel.application.v2`) - formatted using `buf` # Additional Changes None # Additional Context - part of https://github.com/zitadel/zitadel/issues/10772 - requires backport to v4.x
914 lines
31 KiB
Go
914 lines
31 KiB
Go
//go:build integration
|
|
|
|
package app_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
"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"
|
|
"github.com/zitadel/zitadel/pkg/grpc/application/v2"
|
|
"github.com/zitadel/zitadel/pkg/grpc/filter/v2"
|
|
)
|
|
|
|
func TestGetApplication(t *testing.T) {
|
|
p, projectOwnerCtx := getProjectAndProjectContext(t, instance, IAMOwnerCtx)
|
|
|
|
apiAppName := integration.ApplicationName()
|
|
createdApiApp, errAPIAppCreation := instance.Client.ApplicationV2.CreateApplication(IAMOwnerCtx, &application.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: apiAppName,
|
|
ApplicationType: &application.CreateApplicationRequest_ApiConfiguration{
|
|
ApiConfiguration: &application.CreateAPIApplicationRequest{
|
|
AuthMethodType: application.APIAuthMethodType_API_AUTH_METHOD_TYPE_BASIC,
|
|
},
|
|
},
|
|
})
|
|
require.Nil(t, errAPIAppCreation)
|
|
|
|
samlAppName := integration.ApplicationName()
|
|
createdSAMLApp, errSAMLAppCreation := instance.Client.ApplicationV2.CreateApplication(IAMOwnerCtx, &application.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: samlAppName,
|
|
ApplicationType: &application.CreateApplicationRequest_SamlConfiguration{
|
|
SamlConfiguration: &application.CreateSAMLApplicationRequest{
|
|
LoginVersion: &application.LoginVersion{Version: &application.LoginVersion_LoginV1{LoginV1: &application.LoginV1{}}},
|
|
Metadata: &application.CreateSAMLApplicationRequest_MetadataXml{MetadataXml: samlMetadataGen(integration.URL())},
|
|
},
|
|
},
|
|
})
|
|
require.Nil(t, errSAMLAppCreation)
|
|
|
|
oidcAppName := integration.ApplicationName()
|
|
createdOIDCApp, errOIDCAppCreation := instance.Client.ApplicationV2.CreateApplication(IAMOwnerCtx, &application.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: oidcAppName,
|
|
ApplicationType: &application.CreateApplicationRequest_OidcConfiguration{
|
|
OidcConfiguration: &application.CreateOIDCApplicationRequest{
|
|
RedirectUris: []string{"http://example.com"},
|
|
ResponseTypes: []application.OIDCResponseType{application.OIDCResponseType_OIDC_RESPONSE_TYPE_CODE},
|
|
GrantTypes: []application.OIDCGrantType{application.OIDCGrantType_OIDC_GRANT_TYPE_AUTHORIZATION_CODE},
|
|
ApplicationType: application.OIDCApplicationType_OIDC_APP_TYPE_WEB,
|
|
AuthMethodType: application.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_BASIC,
|
|
PostLogoutRedirectUris: []string{"http://example.com/home"},
|
|
Version: application.OIDCVersion_OIDC_VERSION_1_0,
|
|
AccessTokenType: application.OIDCTokenType_OIDC_TOKEN_TYPE_JWT,
|
|
BackChannelLogoutUri: "http://example.com/logout",
|
|
LoginVersion: &application.LoginVersion{Version: &application.LoginVersion_LoginV2{LoginV2: &application.LoginV2{BaseUri: &baseURI}}},
|
|
},
|
|
},
|
|
})
|
|
require.Nil(t, errOIDCAppCreation)
|
|
|
|
t.Parallel()
|
|
|
|
tt := []struct {
|
|
testName string
|
|
inputRequest *application.GetApplicationRequest
|
|
inputCtx context.Context
|
|
|
|
expectedErrorType codes.Code
|
|
expectedAppName string
|
|
expectedAppID string
|
|
expectedApplicationType string
|
|
}{
|
|
{
|
|
testName: "when unknown application ID should return not found error",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &application.GetApplicationRequest{
|
|
ApplicationId: integration.ID(),
|
|
},
|
|
|
|
expectedErrorType: codes.NotFound,
|
|
},
|
|
{
|
|
testName: "when user has no permission should return membership not found error",
|
|
inputCtx: NoPermissionCtx,
|
|
inputRequest: &application.GetApplicationRequest{
|
|
ApplicationId: createdApiApp.GetApplicationId(),
|
|
},
|
|
|
|
expectedErrorType: codes.NotFound,
|
|
},
|
|
{
|
|
testName: "when providing API application ID should return valid API application result",
|
|
inputCtx: projectOwnerCtx,
|
|
inputRequest: &application.GetApplicationRequest{
|
|
ApplicationId: createdApiApp.GetApplicationId(),
|
|
},
|
|
|
|
expectedAppName: apiAppName,
|
|
expectedAppID: createdApiApp.GetApplicationId(),
|
|
expectedApplicationType: fmt.Sprintf("%T", &application.Application_ApiConfiguration{}),
|
|
},
|
|
{
|
|
testName: "when providing SAML application ID should return valid SAML application result",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &application.GetApplicationRequest{
|
|
ApplicationId: createdSAMLApp.GetApplicationId(),
|
|
},
|
|
|
|
expectedAppName: samlAppName,
|
|
expectedAppID: createdSAMLApp.GetApplicationId(),
|
|
expectedApplicationType: fmt.Sprintf("%T", &application.Application_SamlConfiguration{}),
|
|
},
|
|
{
|
|
testName: "when providing OIDC application ID should return valid OIDC application result",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &application.GetApplicationRequest{
|
|
ApplicationId: createdOIDCApp.GetApplicationId(),
|
|
},
|
|
|
|
expectedAppName: oidcAppName,
|
|
expectedAppID: createdOIDCApp.GetApplicationId(),
|
|
expectedApplicationType: fmt.Sprintf("%T", &application.Application_OidcConfiguration{}),
|
|
},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputCtx, time.Minute)
|
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
|
// When
|
|
res, err := instance.Client.ApplicationV2.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.GetApplication().GetApplicationId())
|
|
assert.Equal(t, tc.expectedAppName, res.GetApplication().GetName())
|
|
assert.NotZero(t, res.GetApplication().GetCreationDate())
|
|
assert.NotZero(t, res.GetApplication().GetChangeDate())
|
|
|
|
appType := fmt.Sprintf("%T", res.GetApplication().GetConfiguration())
|
|
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, integration.URL(), p.GetId())
|
|
|
|
createdOIDCApp, oidcAppName := createOIDCAppWithName(t, integration.URL(), p.GetId())
|
|
|
|
type appWithName struct {
|
|
app *application.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.GetApplicationId() < b.app.GetApplicationId() {
|
|
return -1
|
|
}
|
|
if a.app.GetApplicationId() > b.app.GetApplicationId() {
|
|
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 *application.ListApplicationsRequest
|
|
inputCtx context.Context
|
|
|
|
expectedOrderedList []appWithName
|
|
expectedOrderedKeys func(keys []appWithName) any
|
|
actualOrderedKeys func(keys []*application.Application) any
|
|
}{
|
|
{
|
|
testName: "when no apps found should return empty list",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: "another-id"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
expectedOrderedList: []appWithName{},
|
|
expectedOrderedKeys: func(keys []appWithName) any { return keys },
|
|
actualOrderedKeys: func(keys []*application.Application) any { return keys },
|
|
},
|
|
{
|
|
testName: "when user has no read permission should return empty set",
|
|
inputCtx: NoPermissionCtx,
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
expectedOrderedList: []appWithName{},
|
|
expectedOrderedKeys: func(keys []appWithName) any { return keys },
|
|
actualOrderedKeys: func(keys []*application.Application) any { return keys },
|
|
},
|
|
{
|
|
testName: "when sorting by name should return apps sorted by name in descending order",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
SortingColumn: application.ApplicationSorting_APPLICATION_SORT_BY_NAME,
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
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 []*application.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: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
},
|
|
SortingColumn: application.ApplicationSorting_APPLICATION_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 []*application.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: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
},
|
|
SortingColumn: application.ApplicationSorting_APPLICATION_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.GetApplicationId()
|
|
}
|
|
|
|
return ids
|
|
},
|
|
actualOrderedKeys: func(apps []*application.Application) any {
|
|
ids := make([]string, len(apps))
|
|
for i, a := range apps {
|
|
ids[i] = a.GetApplicationId()
|
|
}
|
|
|
|
return ids
|
|
},
|
|
},
|
|
{
|
|
testName: "when sorting by creation date should return apps sorted by creation date in descending order",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
},
|
|
SortingColumn: application.ApplicationSorting_APPLICATION_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 []*application.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: &application.ListApplicationsRequest{
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_StateFilter{
|
|
StateFilter: application.ApplicationState_APPLICATION_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 []*application.Application) any {
|
|
creationDates := make([]time.Time, len(apps))
|
|
for i, a := range apps {
|
|
creationDates[i] = a.GetCreationDate().AsTime()
|
|
}
|
|
|
|
return creationDates
|
|
},
|
|
},
|
|
{
|
|
testName: "when filtering by application type should return apps of matching type only",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
{Filter: &application.ApplicationSearchFilter_TypeFilter{
|
|
TypeFilter: application.ApplicationType_APPLICATION_TYPE_OIDC,
|
|
}},
|
|
},
|
|
},
|
|
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 []*application.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, time.Minute)
|
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
|
// When
|
|
res, err := instance.Client.ApplicationV2.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.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner)
|
|
p, projectOwnerCtx := getProjectAndProjectContext(t, instancePermissionV2, iamOwnerCtx)
|
|
_, otherProjectOwnerCtx := getProjectAndProjectContext(t, instancePermissionV2, iamOwnerCtx)
|
|
|
|
appName1, appName2, appName3 := integration.ApplicationName(), integration.ApplicationName(), integration.ApplicationName()
|
|
reqForAPIAppCreation := &application.CreateApplicationRequest_ApiConfiguration{
|
|
ApiConfiguration: &application.CreateAPIApplicationRequest{AuthMethodType: application.APIAuthMethodType_API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT},
|
|
}
|
|
|
|
app1, appAPIConfigChangeErr := instancePermissionV2.Client.ApplicationV2.CreateApplication(iamOwnerCtx, &application.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: appName1,
|
|
ApplicationType: reqForAPIAppCreation,
|
|
})
|
|
require.Nil(t, appAPIConfigChangeErr)
|
|
|
|
app2, appAPIConfigChangeErr := instancePermissionV2.Client.ApplicationV2.CreateApplication(iamOwnerCtx, &application.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: appName2,
|
|
ApplicationType: reqForAPIAppCreation,
|
|
})
|
|
require.Nil(t, appAPIConfigChangeErr)
|
|
|
|
app3, appAPIConfigChangeErr := instancePermissionV2.Client.ApplicationV2.CreateApplication(iamOwnerCtx, &application.CreateApplicationRequest{
|
|
ProjectId: p.GetId(),
|
|
Name: appName3,
|
|
ApplicationType: reqForAPIAppCreation,
|
|
})
|
|
require.Nil(t, appAPIConfigChangeErr)
|
|
|
|
t.Parallel()
|
|
|
|
tt := []struct {
|
|
testName string
|
|
inputRequest *application.ListApplicationsRequest
|
|
inputCtx context.Context
|
|
|
|
expectedCode codes.Code
|
|
expectedAppIDs []string
|
|
}{
|
|
{
|
|
testName: "when user has no read permission should return empty set",
|
|
inputCtx: instancePermissionV2.WithAuthorizationToken(context.Background(), integration.UserTypeNoPermission),
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
expectedAppIDs: []string{},
|
|
},
|
|
{
|
|
testName: "when projectOwner should return full application list",
|
|
inputCtx: projectOwnerCtx,
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
expectedCode: codes.OK,
|
|
expectedAppIDs: []string{app1.GetApplicationId(), app2.GetApplicationId(), app3.GetApplicationId()},
|
|
},
|
|
{
|
|
testName: "when orgOwner should return full application list",
|
|
inputCtx: instancePermissionV2.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
expectedAppIDs: []string{app1.GetApplicationId(), app2.GetApplicationId(), app3.GetApplicationId()},
|
|
},
|
|
{
|
|
testName: "when iamOwner user should return full application list",
|
|
inputCtx: iamOwnerCtx,
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{ProjectId: p.GetId()},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
expectedAppIDs: []string{app1.GetApplicationId(), app2.GetApplicationId(), app3.GetApplicationId()},
|
|
},
|
|
{
|
|
testName: "when other projectOwner user should return empty list",
|
|
inputCtx: otherProjectOwnerCtx,
|
|
inputRequest: &application.ListApplicationsRequest{
|
|
Filters: []*application.ApplicationSearchFilter{
|
|
{
|
|
Filter: &application.ApplicationSearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ProjectIDFilter{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, time.Minute)
|
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
|
// When
|
|
res, err := instancePermissionV2.Client.ApplicationV2.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.GetApplicationId())
|
|
}
|
|
|
|
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.GetApplicationId(), time.Now().AddDate(0, 0, 1))
|
|
|
|
t.Parallel()
|
|
|
|
tt := []struct {
|
|
testName string
|
|
inputRequest *application.GetApplicationKeyRequest
|
|
inputCtx context.Context
|
|
|
|
expectedErrorType codes.Code
|
|
expectedAppKeyID string
|
|
}{
|
|
{
|
|
testName: "when unknown application ID should return not found error",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &application.GetApplicationKeyRequest{
|
|
KeyId: integration.ID(),
|
|
},
|
|
|
|
expectedErrorType: codes.NotFound,
|
|
},
|
|
{
|
|
testName: "when user has no permission should return membership not found error",
|
|
inputCtx: NoPermissionCtx,
|
|
inputRequest: &application.GetApplicationKeyRequest{
|
|
KeyId: createdAppKey.GetKeyId(),
|
|
},
|
|
|
|
expectedErrorType: codes.NotFound,
|
|
},
|
|
{
|
|
testName: "when providing API application ID should return valid API application result",
|
|
inputCtx: projectOwnerCtx,
|
|
inputRequest: &application.GetApplicationKeyRequest{
|
|
KeyId: createdAppKey.GetKeyId(),
|
|
},
|
|
|
|
expectedAppKeyID: createdAppKey.GetKeyId(),
|
|
},
|
|
{
|
|
testName: "when user is OrgOwner should return request key",
|
|
inputCtx: OrgOwnerCtx,
|
|
inputRequest: &application.GetApplicationKeyRequest{
|
|
KeyId: createdAppKey.GetKeyId(),
|
|
},
|
|
|
|
expectedAppKeyID: createdAppKey.GetKeyId(),
|
|
},
|
|
{
|
|
testName: "when user is IAMOwner should return request key",
|
|
inputCtx: OrgOwnerCtx,
|
|
inputRequest: &application.GetApplicationKeyRequest{
|
|
KeyId: createdAppKey.GetKeyId(),
|
|
},
|
|
|
|
expectedAppKeyID: createdAppKey.GetKeyId(),
|
|
},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputCtx, time.Minute)
|
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
|
// When
|
|
res, err := instance.Client.ApplicationV2.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.GetKeyId())
|
|
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.GetApplicationId(), in2Days)
|
|
appKey2 := createAppKey(t, IAMOwnerCtx, instance, p.GetId(), createdApiApp1.GetApplicationId(), in3Days)
|
|
appKey3 := createAppKey(t, IAMOwnerCtx, instance, p.GetId(), createdApiApp1.GetApplicationId(), tomorrow)
|
|
appKey4 := createAppKey(t, IAMOwnerCtx, instance, p.GetId(), createdApiApp2.GetApplicationId(), tomorrow)
|
|
|
|
t.Parallel()
|
|
|
|
tt := []struct {
|
|
testName string
|
|
inputRequest *application.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: &application.ListApplicationKeysRequest{
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
SortingColumn: application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_EXPIRATION,
|
|
Filters: []*application.ApplicationKeySearchFilter{
|
|
{Filter: &application.ApplicationKeySearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ApplicationKeyProjectIDFilter{ProjectId: p.GetId()},
|
|
}},
|
|
},
|
|
},
|
|
expectedAppKeysIDs: []string{appKey3.GetKeyId(), appKey4.GetKeyId(), appKey1.GetKeyId(), appKey2.GetKeyId()},
|
|
},
|
|
{
|
|
testName: "when sorting by creation date should return keys sorted by creation date descending",
|
|
inputCtx: IAMOwnerCtx,
|
|
inputRequest: &application.ListApplicationKeysRequest{
|
|
SortingColumn: application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_CREATION_DATE,
|
|
Filters: []*application.ApplicationKeySearchFilter{
|
|
{Filter: &application.ApplicationKeySearchFilter_ProjectIdFilter{
|
|
ProjectIdFilter: &application.ApplicationKeyProjectIDFilter{ProjectId: p.GetId()},
|
|
}},
|
|
},
|
|
},
|
|
expectedAppKeysIDs: []string{appKey4.GetKeyId(), appKey3.GetKeyId(), appKey2.GetKeyId(), appKey1.GetKeyId()},
|
|
},
|
|
{
|
|
testName: "when filtering by application ID should return keys matching application ID sorted by ID",
|
|
inputCtx: projectOwnerCtx,
|
|
inputRequest: &application.ListApplicationKeysRequest{
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
Filters: []*application.ApplicationKeySearchFilter{
|
|
{Filter: &application.ApplicationKeySearchFilter_ApplicationIdFilter{
|
|
ApplicationIdFilter: &application.ApplicationKeyApplicationIDFilter{ApplicationId: createdApiApp1.GetApplicationId()},
|
|
}},
|
|
},
|
|
},
|
|
expectedAppKeysIDs: []string{appKey1.GetKeyId(), appKey2.GetKeyId(), appKey3.GetKeyId()},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputCtx, time.Minute)
|
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
|
// When
|
|
res, err := instance.Client.ApplicationV2.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.GetKeyId())
|
|
}
|
|
}
|
|
}, retryDuration, tick)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListApplicationKeys_WithPermissionV2(t *testing.T) {
|
|
ensureFeaturePermissionV2Enabled(t, instancePermissionV2)
|
|
iamOwnerCtx := instancePermissionV2.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner)
|
|
loginUserCtx := instancePermissionV2.WithAuthorizationToken(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.GetApplicationId(), in2Days)
|
|
appKey2 := createAppKey(t, iamOwnerCtx, instancePermissionV2, p.GetId(), createdApiApp1.GetApplicationId(), in3Days)
|
|
appKey3 := createAppKey(t, iamOwnerCtx, instancePermissionV2, p.GetId(), createdApiApp1.GetApplicationId(), tomorrow)
|
|
appKey4 := createAppKey(t, iamOwnerCtx, instancePermissionV2, p.GetId(), createdApiApp2.GetApplicationId(), tomorrow)
|
|
|
|
t.Parallel()
|
|
|
|
tt := []struct {
|
|
testName string
|
|
inputRequest *application.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: &application.ListApplicationKeysRequest{
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
SortingColumn: application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_EXPIRATION,
|
|
},
|
|
expectedAppKeysIDs: []string{appKey3.GetKeyId(), appKey4.GetKeyId(), appKey1.GetKeyId(), appKey2.GetKeyId()},
|
|
},
|
|
{
|
|
testName: "when sorting by creation date should return keys sorted by creation date descending",
|
|
inputCtx: iamOwnerCtx,
|
|
inputRequest: &application.ListApplicationKeysRequest{
|
|
SortingColumn: application.ApplicationKeysSorting_APPLICATION_KEYS_SORT_BY_CREATION_DATE,
|
|
},
|
|
expectedAppKeysIDs: []string{appKey4.GetKeyId(), appKey3.GetKeyId(), appKey2.GetKeyId(), appKey1.GetKeyId()},
|
|
},
|
|
{
|
|
testName: "when filtering by application ID should return keys matching application ID sorted by ID",
|
|
inputCtx: projectOwnerCtx,
|
|
inputRequest: &application.ListApplicationKeysRequest{
|
|
Pagination: &filter.PaginationRequest{Asc: true},
|
|
Filters: []*application.ApplicationKeySearchFilter{
|
|
{Filter: &application.ApplicationKeySearchFilter_ApplicationIdFilter{
|
|
ApplicationIdFilter: &application.ApplicationKeyApplicationIDFilter{ApplicationId: createdApiApp1.GetApplicationId()},
|
|
}},
|
|
},
|
|
},
|
|
expectedAppKeysIDs: []string{appKey1.GetKeyId(), appKey2.GetKeyId(), appKey3.GetKeyId()},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
// t.Parallel()
|
|
|
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputCtx, time.Minute)
|
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
|
// When
|
|
res, err := instancePermissionV2.Client.ApplicationV2.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.GetKeyId())
|
|
}
|
|
}
|
|
}, retryDuration, tick)
|
|
})
|
|
}
|
|
}
|