Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into bugfix/acp-4499-mes…
Browse files Browse the repository at this point in the history
…sage-broker-conditional-channels-handling
  • Loading branch information
pushokwhite committed Dec 19, 2024
2 parents 3462ed8 + b816e3c commit 95ad24a
Show file tree
Hide file tree
Showing 12 changed files with 498 additions and 6 deletions.
124 changes: 124 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,127 @@ With these commands you've set up the AppKernel and can start the tests
vendor/bin/codecept build
vendor/bin/codecept run
```

# Documentation

## Configuration
### AppIdentifier

Every App needs a unique App Identifier which needs to be configured in the `config/Shared/config_default.php` file after installation.

```
use Spryker\Shared\AppKernel\AppConstants;
$config[AppConstants::APP_IDENTIFIER] = getenv('APP_IDENTIFIER') ?: '1ba6db00-d12c-43c9-9783-936e4cded397';
```

## High-Level Architecture

[<img alt="alt_text" width="auto" src="docs/images/app-kernel-high-level-architecture.svg" />](https://docs.spryker.com/)

## Features

### Encryption
This package comes with a built-in Encryption for data that gets persisted. By default, the encryption is disabled but can be easily enabled. After installation and setup, you can configure the encryption of sensitive data.

### Enable Plugin
The encryption will be done with the help of a plugin. You can add the required plugin to the `\Pyz\Client\SecretsManager\SecretsManagerDependencyProvider::getSecretsManagerProviderPlugin()` method on the project level.

Currently, we have an AWS Plugin available for you `\Spryker\Client\SecretsManagerAws\Plugin\SecretsManager\SecretsManagerAwsProviderPlugin` for any other encryption you need to create your own plugin.

You can install the package that contains the plugin with

```
composer require spryker/secrets-manager-aws
```

### Extend the Propel schema

Add a `spy_app_kernel.schema.xml` on the project level to `src/Pyz/AppKernel/Zed/Persistence/Propel/schema` and update the columns you want to be encrypted.

Example

```
<?xml version="1.0"?>
<database xmlns="spryker:schema-01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="zed" xsi:schemaLocation="spryker:schema-01 https://static.spryker.com/schema-01.xsd" namespace="Orm\Zed\AppKernel\Persistence" package="src.Orm.Zed.AppKernel.Persistence">
<table name="spy_app_config" identifierQuoting="true">
<behavior name="encryption">
<parameter name="column_name_1" value="config"/>
</behavior>
</table>
</database>
```

Next, execute the migrations by running the `docker/sdk console propel:install` command, then these columns will be encrypted.

## Plugins

This package provides the following plugins

### Glue

- `\Spryker\Glue\AppKernel\Plugin\RouteProvider\AppKernelRouteProviderPlugin`

#### AppKernelRouteProviderPlugin

This plugin must be added to the `\Pyz\Glue\GlueBackendApiApplication\GlueBackendApiApplicationDependencyProvider::getRouteProviderPlugins()`.

## Extension

In most cases, the default behavior of the AppKernel will be enough. When you need to add or change behavior we provide extension points you can use to change the AppKernel.

This package provides the following extensions.

### Glue

- Configuration validation plugins
- Disconnect validation plugins

#### Configuration validation plugins

This plugin stack can be used to add your own validators for the configuration of the App. This will most likely be useful when you need to validate the passed configuration from the App Store Catalog e.g. checking against the implemented provider if credentials are valid.

You can add your own plugins to the `\Pyz\Glue\AppKernel\AppKernelDependencyProvider::getRequestConfigureValidatorPlugins()` method on the project level.

#### Disconnect validation plugins

This plugin stack can be used to add your own validators for the disconnect of the App. This can be used e.g. for validating if the App can be disconnected or not.

You can add your own plugins to the `\Pyz\Glue\AppKernel\AppKernelDependencyProvider::getRequestDisconnectValidatorPlugins()` method on the project level.

## Zed

Zed offers the following extension points

- `\Spryker\Zed\AppKernelExtension\Dependency\Plugin\AppKernelPlatformPluginInterface`
- `\Spryker\Zed\AppKernelExtension\Dependency\Plugin\ConfigurationBeforeSavePluginInterface`
- `\Spryker\Zed\AppKernelExtension\Dependency\Plugin\ConfigurationAfterSavePluginInterface`
- `\Spryker\Zed\AppKernelExtension\Dependency\Plugin\ConfigurationBeforeDeletePluginInterface`
- `\Spryker\Zed\AppKernelExtension\Dependency\Plugin\ConfigurationAfterDeletePluginInterface`

Both save and delete actions can be extended with a plugin that needs to be executed before and after the AppKernel code is executed.

### AppKernelPlatformPluginInterface

You can implement this plugin in the Apps PlatformPlugin. It enables the validation of the configuration that gets passed from the AppStoreCatalog.

### ConfigurationBeforeSavePluginInterface

You can add your plugins to the `\Pyz\Zed\AppKernel\AppKernelDependencyProvider::getConfigurationBeforeSavePlugins()` method on the project level.

### ConfigurationAfterSavePluginInterface

You can add your plugins to the `\Pyz\Zed\AppKernel\AppKernelDependencyProvider::getConfigurationAfterSavePlugins()` method on the project level.

### ConfigurationBeforeDeletePluginInterface

You can add your plugins to the `\Pyz\Zed\AppKernel\AppKernelDependencyProvider::getConfigurationBeforeDeletePlugins()` method on the project level.

### ConfigurationAfterDeletePluginInterface

You can add your plugins to the `\Pyz\Zed\AppKernel\AppKernelDependencyProvider::getConfigurationAfterDeletePlugins()` method on the project level.



4 changes: 4 additions & 0 deletions docs/images/app-kernel-high-level-architecture.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
<exclude name="SlevomatCodingStandard.Classes.DisallowConstructorPropertyPromotion.DisallowedConstructorPropertyPromotion"/>
</rule>

<rule ref="SlevomatCodingStandard.Functions.DisallowNamedArguments">
<exclude name="SlevomatCodingStandard.Functions.DisallowNamedArguments.DisallowedNamedArgument"/>
</rule>

<!-- PHP 8.0 null safe operator -->
<rule ref="SlevomatCodingStandard.ControlStructures.DisallowNullSafeObjectOperator">
<exclude name="SlevomatCodingStandard.ControlStructures.DisallowNullSafeObjectOperator"/>
Expand Down
23 changes: 23 additions & 0 deletions src/Spryker/Glue/AppKernel/AppKernelConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,27 @@ public function getOpenApiSchemaPath(): ?string
{
return $this->getConfig()->hasKey(AppKernelConstants::OPEN_API_SCHEMA_PATH) ? $this->getConfig()->get(AppKernelConstants::OPEN_API_SCHEMA_PATH) : null;
}

/**
* @api
*
* @return array<int, string>
*/
public function getOpenApiSchemaRequestValidationExcludedPaths(): array
{
return [];
}

/**
* @api
*
* @return array<int, string>
*/
public function getAppConfigRequestValidationExcludedPaths(): array
{
return [
static::CONFIGURE_ROUTE_PATH,
static::DISCONNECT_ROUTE_PATH,
];
}
}
6 changes: 6 additions & 0 deletions src/Spryker/Glue/AppKernel/AppKernelFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Spryker\Glue\AppKernel\Dependency\Service\AppKernelToUtilEncodingServiceInterface;
use Spryker\Glue\AppKernel\Mapper\GlueRequestMapper;
use Spryker\Glue\AppKernel\Mapper\GlueRequestMapperInterface;
use Spryker\Glue\AppKernel\Validator\AppConfigForTenantValidator;
use Spryker\Glue\AppKernel\Validator\BodyStructureValidator;
use Spryker\Glue\AppKernel\Validator\ConfigurationValidator;
use Spryker\Glue\AppKernel\Validator\HeaderValidator;
Expand Down Expand Up @@ -118,4 +119,9 @@ public function createOpenApiRequestSchemaValidator(): OpenApiRequestSchemaValid
{
return new OpenApiRequestSchemaValidator($this->getConfig());
}

public function createAppConfigForTenantValidator(): AppConfigForTenantValidator
{
return new AppConfigForTenantValidator($this->getAppKernelFacade(), $this->getConfig());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

namespace Spryker\Glue\AppKernel\Plugin\GlueApplication;

use Generated\Shared\Transfer\GlueRequestTransfer;
use Generated\Shared\Transfer\GlueRequestValidationTransfer;
use Spryker\Glue\GlueApplicationExtension\Dependency\Plugin\RequestValidatorPluginInterface;
use Spryker\Glue\Kernel\AbstractPlugin;

/**
* @method \Spryker\Glue\AppKernel\AppKernelFactory getFactory()
*/
class AppConfigForTenantValidatorPlugin extends AbstractPlugin implements RequestValidatorPluginInterface
{
public function validate(GlueRequestTransfer $glueRequestTransfer): GlueRequestValidationTransfer
{
return $this->getFactory()->createAppConfigForTenantValidator()->validate($glueRequestTransfer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

namespace Spryker\Glue\AppKernel\Validator;

use Generated\Shared\Transfer\AppConfigCriteriaTransfer;
use Generated\Shared\Transfer\GlueErrorTransfer;
use Generated\Shared\Transfer\GlueRequestTransfer;
use Generated\Shared\Transfer\GlueRequestValidationTransfer;
use Spryker\Glue\AppKernel\AppKernelConfig;
use Spryker\Glue\AppKernel\Dependency\Facade\AppKernelToAppKernelFacadeInterface;
use Spryker\Shared\Log\LoggerTrait;
use Spryker\Zed\AppKernel\AppKernelConfig as AppKernelAppKernelConfig;
use Symfony\Component\HttpFoundation\Response;
use Throwable;

class AppConfigForTenantValidator
{
use LoggerTrait;

public function __construct(protected AppKernelToAppKernelFacadeInterface $appKernelToAppKernelFacade, protected AppKernelConfig $appKernelConfig)
{
}

public function validate(GlueRequestTransfer $glueRequestTransfer): GlueRequestValidationTransfer
{
$glueRequestValidationTransfer = new GlueRequestValidationTransfer();
$glueRequestValidationTransfer->setIsValid(true);

if ($this->isPathExcludedFromValidation($glueRequestTransfer)) {
return $glueRequestValidationTransfer;
}

$tenantIdentifier = $this->getTenantIdentifier($glueRequestTransfer);

$appConfigCriteriaTransfer = new AppConfigCriteriaTransfer();
$appConfigCriteriaTransfer->setTenantIdentifier($tenantIdentifier);

try {
$appConfigTransfer = $this->appKernelToAppKernelFacade->getConfig($appConfigCriteriaTransfer);
if ($appConfigTransfer->getStatus() === AppKernelAppKernelConfig::APP_STATUS_DISCONNECTED) {
$glueErrorTransfer = new GlueErrorTransfer();
$glueErrorTransfer->setMessage('Tenant is disconnected.');

$glueRequestValidationTransfer
->setIsValid(false)
->setStatus(Response::HTTP_FORBIDDEN)
->addError($glueErrorTransfer);
}
} catch (Throwable $throwable) {
$this->getLogger()->error(
$throwable->getMessage(),
$glueRequestTransfer->toArray(),
);

$glueErrorTransfer = new GlueErrorTransfer();
$glueErrorTransfer
->setMessage($throwable->getMessage());

$glueRequestValidationTransfer
->setIsValid(false)
->addError($glueErrorTransfer)
->setStatus(Response::HTTP_INTERNAL_SERVER_ERROR);

return $glueRequestValidationTransfer;
}

return $glueRequestValidationTransfer;
}

protected function isPathExcludedFromValidation(GlueRequestTransfer $glueRequestTransfer): bool
{
return in_array($glueRequestTransfer->getPath(), $this->appKernelConfig->getAppConfigRequestValidationExcludedPaths());
}

protected function getTenantIdentifier(GlueRequestTransfer $glueRequestTransfer): ?string
{
$meta = $glueRequestTransfer->getMeta();

if (!isset($meta['x-tenant-identifier'])) {
return null;
}

$tenantIdentifier = $meta['x-tenant-identifier'];

if (is_array($tenantIdentifier)) {
return $tenantIdentifier[0];
}

return $tenantIdentifier;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public function validate(GlueRequestTransfer $glueRequestTransfer): GlueRequestV
$glueRequestValidationTransfer = new GlueRequestValidationTransfer();
$glueRequestValidationTransfer->setIsValid(true);

if ($this->isPathExcludedFromValidation($glueRequestTransfer)) {
return $glueRequestValidationTransfer;
}

$openApiSchemaPath = $this->appKernelConfig->getOpenApiSchemaPath();

if ($openApiSchemaPath === null || $openApiSchemaPath === '' || $openApiSchemaPath === '0') {
Expand Down Expand Up @@ -73,6 +77,11 @@ public function validate(GlueRequestTransfer $glueRequestTransfer): GlueRequestV
return $glueRequestValidationTransfer;
}

protected function isPathExcludedFromValidation(GlueRequestTransfer $glueRequestTransfer): bool
{
return in_array($glueRequestTransfer->getPath(), $this->appKernelConfig->getOpenApiSchemaRequestValidationExcludedPaths());
}

protected function getMessageFromThrowable(Throwable $throwable): string
{
return match (get_class($throwable)) {
Expand Down
4 changes: 0 additions & 4 deletions src/Spryker/Zed/AppKernel/Persistence/AppKernelRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ public function findAppConfigByCriteria(
if ($appConfigEntity === null) {
$errorMessage = 'Could not find an App configuration for the given Tenant';

$this->getLogger()->error($errorMessage, [
'tenantIdentifier' => $appConfigCriteriaTransfer->getTenantIdentifierOrFail(),
]);

throw new AppConfigNotFoundException($errorMessage);
}

Expand Down
Loading

0 comments on commit 95ad24a

Please sign in to comment.