feat: App API v2 (#10077)

# Which Problems Are Solved

This PR *partially* addresses #9450 . Specifically, it implements the
resource based API for the apps. APIs for app keys ARE not part of this
PR.

# How the Problems Are Solved

- `CreateApplication`, `PatchApplication` (update) and
`RegenerateClientSecret` endpoints are now unique for all app types:
API, SAML and OIDC apps.
  - All new endpoints have integration tests
  - All new endpoints are using permission checks V2

# Additional Changes

- The `ListApplications` endpoint allows to do sorting (see protobuf for
details) and filtering by app type (see protobuf).
- SAML and OIDC update endpoint can now receive requests for partial
updates

# Additional Context

Partially addresses #9450
This commit is contained in:
Marco A.
2025-06-27 17:25:44 +02:00
committed by GitHub
parent 016676e1dc
commit 2691dae2b6
48 changed files with 6845 additions and 603 deletions

View File

@@ -90,16 +90,24 @@ func (c *Commands) AddAPIApplication(ctx context.Context, apiApp *domain.APIApp,
return nil, zerrors.ThrowInvalidArgument(nil, "PROJECT-5m9E", "Errors.Project.App.Invalid")
}
if _, err := c.checkProjectExists(ctx, apiApp.AggregateID, resourceOwner); err != nil {
projectResOwner, err := c.checkProjectExists(ctx, apiApp.AggregateID, resourceOwner)
if err != nil {
return nil, err
}
if resourceOwner == "" {
resourceOwner = projectResOwner
}
if !apiApp.IsValid() {
return nil, zerrors.ThrowInvalidArgument(nil, "PROJECT-Bff2g", "Errors.Project.App.Invalid")
}
appID, err := c.idGenerator.Next()
if err != nil {
return nil, err
appID := apiApp.AppID
if appID == "" {
appID, err = c.idGenerator.Next()
if err != nil {
return nil, err
}
}
return c.addAPIApplicationWithID(ctx, apiApp, resourceOwner, appID)
@@ -112,6 +120,13 @@ func (c *Commands) addAPIApplicationWithID(ctx context.Context, apiApp *domain.A
apiApp.AppID = appID
addedApplication := NewAPIApplicationWriteModel(apiApp.AggregateID, resourceOwner)
if err := c.eventstore.FilterToQueryReducer(ctx, addedApplication); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateApplication(ctx, addedApplication.ResourceOwner, addedApplication.AggregateID); err != nil {
return nil, err
}
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
events := []eventstore.Command{
@@ -150,7 +165,7 @@ func (c *Commands) addAPIApplicationWithID(ctx context.Context, apiApp *domain.A
return result, nil
}
func (c *Commands) ChangeAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string) (*domain.APIApp, error) {
func (c *Commands) UpdateAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string) (*domain.APIApp, error) {
if apiApp.AppID == "" || apiApp.AggregateID == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-1m900", "Errors.Project.App.APIConfigInvalid")
}
@@ -165,6 +180,13 @@ func (c *Commands) ChangeAPIApplication(ctx context.Context, apiApp *domain.APIA
if !existingAPI.IsAPI() {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Gnwt3", "Errors.Project.App.IsNotAPI")
}
if err := c.eventstore.FilterToQueryReducer(ctx, existingAPI); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateApplication(ctx, existingAPI.ResourceOwner, existingAPI.AggregateID); err != nil {
return nil, err
}
projectAgg := ProjectAggregateFromWriteModel(&existingAPI.WriteModel)
changedEvent, hasChanged, err := existingAPI.NewChangedEvent(
ctx,
@@ -205,6 +227,11 @@ func (c *Commands) ChangeAPIApplicationSecret(ctx context.Context, projectID, ap
if !existingAPI.IsAPI() {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-aeH4", "Errors.Project.App.IsNotAPI")
}
if err := c.checkPermissionUpdateApplication(ctx, existingAPI.ResourceOwner, existingAPI.AggregateID); err != nil {
return nil, err
}
encodedHash, plain, err := c.newHashedSecret(ctx, c.eventstore.Filter) //nolint:staticcheck
if err != nil {
return nil, err