Skip to content

Commit

Permalink
MERGE: Merge branch '7.1' into 7.2
Browse files Browse the repository at this point in the history
  • Loading branch information
kdambekalns committed Oct 26, 2022
2 parents df9122b + 445b520 commit 1531a81
Show file tree
Hide file tree
Showing 17 changed files with 262 additions and 24 deletions.
28 changes: 28 additions & 0 deletions .doctum.php
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);

use Doctum\Doctum;
use Doctum\RemoteRepository\GitHubRemoteRepository;
use Symfony\Component\Finder\Finder;

$iterator = Finder::create()
->files()
->name('*.php')
->path('/Classes/')
->in(__DIR__);

return new Doctum($iterator, [
'title' => 'Flow Framework',
'base_url' => 'https://neos.github.io/',
'favicon' => 'https://www.neos.io/favicon-32x32.png',
'language' => 'en',
'remote_repository' => new GitHubRemoteRepository('neos/flow-development-collection', __DIR__),
'footer_link' => [
'href' => 'https://flow.neos.io',
'rel' => 'noreferrer noopener',
'target' => '_blank',
'before_text' => 'Learn more about the',
'link_text' => 'Flow Framework',
'after_text' => 'if you like!',
]
]);
49 changes: 49 additions & 0 deletions .github/workflows/doctum.yml
@@ -0,0 +1,49 @@
name: Build API documentation

on:
workflow_dispatch:
push:
branches: [ '[0-9]+.[0-9]' ]

jobs:
build-api-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Build API documentation
uses: sudo-bot/action-doctum@v5
with:
config-file: .doctum.php
method: "update"
# use of --only-version fixes branch name in "View source" links to GitHub
cli-args: "--output-format=github --no-ansi --no-progress --ignore-parse-errors --only-version=${{ github.ref_name }}"

- name: Check out documentation site
uses: actions/checkout@v3
with:
repository: neos/neos.github.io
path: docs-site

- name: Move rendered docs to site
run: |
rm -rf docs-site/flow/${{ github.ref_name }}
mkdir -p docs-site/flow/${{ github.ref_name }}
mv build/* docs-site/flow/${{ github.ref_name }}/
- name: Commit update
run: |
cd docs-site
git config --local --unset-all "http.https://github.com/.extraheader"
git config --global user.email "ops@neos.io"
git config --global user.name "Neos Bot"
git add .
git commit -m "TASK: Update API docs from ${{ github.ref_name }}"
- name: Push to git
uses: ad-m/github-push-action@v0.6.0
with:
github_token: ${{ secrets.NEOS_BOT_TOKEN }}
repository: neos/neos.github.io
directory: docs-site
branch: main
14 changes: 4 additions & 10 deletions Neos.Cache/Classes/Backend/PdoBackend.php
Expand Up @@ -160,7 +160,6 @@ public function set(string $entryIdentifier, string $data, array $tags = [], int

$lifetime = ($lifetime === null) ? $this->defaultLifetime : $lifetime;


$this->databaseHandle->beginTransaction();
try {
$this->removeWithoutTransaction($entryIdentifier);
Expand Down Expand Up @@ -537,16 +536,11 @@ public function rewind()

$statementHandle = $this->databaseHandle->prepare('SELECT "identifier", "content" FROM "' . $this->cacheTableName . '" WHERE "context"=? AND "cache"=?' . $this->getNotExpiredStatement());
$statementHandle->execute([$this->context(), $this->cacheIdentifier]);
$fetchedColumns = $statementHandle->fetchAll();

foreach ($fetchedColumns as $fetchedColumn) {
// Convert hexadecimal data into binary string,
// because it is not allowed to store null bytes in PostgreSQL.
if ($this->pdoDriver === 'pgsql') {
$fetchedColumn['content'] = hex2bin($fetchedColumn['content']);
}
$statementHandle->bindColumn(1, $identifier);
$statementHandle->bindColumn(2, $content, \PDO::PARAM_LOB);

$cacheEntries[$fetchedColumn['identifier']] = $fetchedColumn['content'];
while ($statementHandle->fetch(\PDO::FETCH_BOUND)) {
$cacheEntries[$identifier] = is_resource($content) ? stream_get_contents($content) : $content;
}

$this->cacheEntriesIterator = new \ArrayIterator($cacheEntries);
Expand Down
28 changes: 27 additions & 1 deletion Neos.Cache/Tests/Functional/Backend/PdoBackendTest.php
Expand Up @@ -13,6 +13,7 @@
* source code.
*/

use Neos\Cache\Backend\BackendInterface;
use Neos\Cache\Backend\PdoBackend;
use Neos\Cache\EnvironmentConfiguration;
use Neos\Cache\Tests\BaseTestCase;
Expand Down Expand Up @@ -58,14 +59,39 @@ public function backendsToTest(): array
* @test
* @dataProvider backendsToTest
*/
public function setAddsCacheEntry($backend)
public function setAddsCacheEntry(BackendInterface $backend): void
{
$backend->flush();

// use data that contains binary junk
$data = random_bytes(2048);
$backend->set('some_entry', $data);
self::assertEquals($data, $backend->get('some_entry'));
}

/**
* @test
* @dataProvider backendsToTest
*/
public function cacheEntriesCanBeIterated(BackendInterface $backend): void
{
$backend->flush();

// use data that contains binary junk
$data = random_bytes(128);
$backend->set('first_entry', $data);
$backend->set('second_entry', $data);
$backend->set('third_entry', $data);

$entries = 0;
foreach ($backend as $entry) {
self::assertEquals($data, $entry);
$entries++;
}

self::assertEquals(3, $entries);
}

private function setupBackends(): void
{
try {
Expand Down
2 changes: 1 addition & 1 deletion Neos.Flow/Classes/Core/Booting/Scripts.php
Expand Up @@ -819,7 +819,7 @@ public static function buildPhpCommand(array $settings): string
* Compares the realpath of the configured PHP binary (if any) with the one flow was called with in a CLI request.
* This avoids config errors where users forget to set Neos.Flow.core.phpBinaryPathAndFilename in CLI.
*
* @param string phpBinaryPathAndFilename
* @param string $phpBinaryPathAndFilename
* @throws FlowException
*/
protected static function ensureCLISubrequestsUseCurrentlyRunningPhpBinary($phpBinaryPathAndFilename)
Expand Down
3 changes: 3 additions & 0 deletions Neos.Flow/Classes/Package.php
Expand Up @@ -25,6 +25,7 @@
use Neos\Flow\Security\Authentication\Token\SessionlessTokenInterface;
use Neos\Flow\Security\Authentication\TokenInterface;
use Neos\Flow\Security\Context;
use Neos\Flow\Security\Cryptography\PrecomposedHashProvider;

/**
* The Flow Package
Expand Down Expand Up @@ -77,6 +78,8 @@ public function boot(Core\Bootstrap $bootstrap)
});
$dispatcher->connect(Cli\SlaveRequestHandler::class, 'dispatchedCommandLineSlaveRequest', Persistence\PersistenceManagerInterface::class, 'persistAll', false);

$dispatcher->connect(Command\CacheCommandController::class, 'warmupCaches', PrecomposedHashProvider::class, 'precomposeHash');

if (!$context->isProduction()) {
$dispatcher->connect(Core\Booting\Sequence::class, 'afterInvokeStep', function (Step $step) use ($bootstrap, $dispatcher) {
if ($step->getIdentifier() === 'neos.flow:resources') {
Expand Down
Expand Up @@ -18,6 +18,7 @@
use Neos\Flow\Reflection\ClassSchema;
use Neos\Flow\Reflection\ReflectionService;
use Neos\Flow\Validation\ValidatorResolver;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling;

/**
Expand Down Expand Up @@ -89,20 +90,21 @@ private function deduplicateValueObjectInsertions()
$entityInsertions = $unitOfWork->getScheduledEntityInsertions();

$knownValueObjects = [];
foreach ($entityInsertions as $entity) {
foreach ($entityInsertions as $oid => $entity) {
$className = TypeHandling::getTypeForValue($entity);
$classSchema = $this->reflectionService->getClassSchema($className);
if ($classSchema !== null && $classSchema->getModelType() === ClassSchema::MODELTYPE_VALUEOBJECT) {
$identifier = $this->persistenceManager->getIdentifierByObject($entity);

if (isset($knownValueObjects[$className][$identifier]) || $unitOfWork->getEntityPersister($className)->exists($entity)) {
$unitOfWork->scheduleForDelete($entity);
unset($entityInsertions[$oid]);
continue;
}

$knownValueObjects[$className][$identifier] = true;
}
}
ObjectAccess::setProperty($unitOfWork, 'entityInsertions', $entityInsertions, true);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion Neos.Flow/Classes/Persistence/QueryInterface.php
Expand Up @@ -160,7 +160,7 @@ public function setLimit(?int $limit): QueryInterface;
/**
* Returns the maximum size of the result set to limit.
*
* @return integer
* @return integer|null
* @api
*/
public function getLimit(): ?int;
Expand Down
2 changes: 1 addition & 1 deletion Neos.Flow/Classes/Reflection/MethodReflection.php
Expand Up @@ -21,7 +21,7 @@
class MethodReflection extends \ReflectionMethod
{
/**
* @var DocCommentParser: An instance of the doc comment parser
* @var DocCommentParser An instance of the doc comment parser
*/
protected $docCommentParser;

Expand Down
2 changes: 1 addition & 1 deletion Neos.Flow/Classes/Reflection/PropertyReflection.php
Expand Up @@ -21,7 +21,7 @@
class PropertyReflection extends \ReflectionProperty
{
/**
* @var DocCommentParser: An instance of the doc comment parser
* @var DocCommentParser An instance of the doc comment parser
*/
protected $docCommentParser;

Expand Down
Expand Up @@ -17,6 +17,7 @@
use Neos\Flow\Security\Authentication\Token\UsernamePasswordTokenInterface;
use Neos\Flow\Security\Authentication\TokenInterface;
use Neos\Flow\Security\Context;
use Neos\Flow\Security\Cryptography\PrecomposedHashProvider;
use Neos\Flow\Security\Cryptography\HashService;
use Neos\Flow\Security\Exception\UnsupportedAuthenticationTokenException;

Expand Down Expand Up @@ -51,6 +52,14 @@ class PersistedUsernamePasswordProvider extends AbstractProvider
*/
protected $persistenceManager;

/**
* The PrecomposedHashProvider has to be injected non-lazy to prevent timing differences
*
* @var PrecomposedHashProvider
* @Flow\Inject(lazy=false)
*/
protected $precomposedHashProvider;

/**
* Returns the class names of the tokens this provider can authenticate.
*
Expand Down Expand Up @@ -99,8 +108,8 @@ public function authenticate(TokenInterface $authenticationToken)
$authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS);

if ($account === null) {
// validate the account anyways (with a dummy salt) in order to prevent timing attacks on this provider
$this->hashService->validatePassword($password, 'bcrypt=>$2a$16$RW.NZM/uP3mC8rsXKJGuN.2pG52thRp5w39NFO.ShmYWV7mJQp0rC');
// validate anyways (with a precomposed hash) in order to prevent timing attacks on this provider
$this->hashService->validatePassword($password, $this->precomposedHashProvider->getPrecomposedHash());
return;
}

Expand Down
@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);

namespace Neos\Flow\Security\Cryptography;

/*
* This file is part of the Neos.Flow package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\Cache\Frontend\StringFrontend;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Utility\Algorithms as UtilityAlgorithms;

/**
* Precomposes a hash to be used to prevent timing attacks
*
* @Flow\Scope("singleton")
*/
class PrecomposedHashProvider
{
/**
* @var HashService
* @Flow\Inject
*/
protected $hashService;

/**
* The Cache have to be injected non-lazy to prevent timing differences
*
* @var StringFrontend
* @Flow\Inject(lazy=false)
*/
protected $cache;

public function getPrecomposedHash(): string
{
$hash = $this->cache->get('precomposed_hash');
if (!$hash) {
$hash = $this->precomposeHash();
}

return $hash;
}

public function precomposeHash(): string
{
$randomPassword = UtilityAlgorithms::generateRandomString(16, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./');
$hash = $this->hashService->hashPassword($randomPassword);
$this->cache->set('precomposed_hash', $hash);
return $hash;
}
}
3 changes: 3 additions & 0 deletions Neos.Flow/Configuration/Caches.yaml
Expand Up @@ -121,6 +121,9 @@ Flow_Security_Cryptography_HashService:
persistent: true
backendOptions:
defaultLifetime: 0
Flow_Security_Cryptography_PrecomposedHashProvider:
frontend: Neos\Cache\Frontend\StringFrontend
backend: Neos\Cache\Backend\SimpleFileBackend


# Flow_Session_*
Expand Down
11 changes: 11 additions & 0 deletions Neos.Flow/Configuration/Objects.yaml
Expand Up @@ -409,6 +409,17 @@ Neos\Flow\Security\Cryptography\BCryptHashingStrategy:
1:
setting: Neos.Flow.security.cryptography.BCryptHashingStrategy.cost

Neos\Flow\Security\Cryptography\PrecomposedHashProvider:
scope: singleton
properties:
cache:
object:
factoryObjectName: Neos\Flow\Cache\CacheManager
factoryMethodName: getCache
arguments:
1:
value: Flow_Security_Cryptography_PrecomposedHashProvider

Neos\Flow\Security\Authorization\Privilege\Method\MethodTargetExpressionParser:
scope: singleton

Expand Down

0 comments on commit 1531a81

Please sign in to comment.