Skip to content

Commit c7afb1f

Browse files
Merge branch 'AC-13833' into AC-13833-AC-13547
2 parents 982b1c4 + 2f564c7 commit c7afb1f

File tree

5 files changed

+292
-114
lines changed

5 files changed

+292
-114
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Csp\Model\SubresourceIntegrity\Storage;
9+
10+
use Psr\Log\LoggerInterface;
11+
use Magento\Framework\Filesystem;
12+
use Magento\Framework\App\Filesystem\DirectoryList;
13+
use Magento\Framework\Exception\FileSystemException;
14+
use Magento\Csp\Model\SubresourceIntegrity\StorageInterface;
15+
16+
/**
17+
* Filesystem based SRI hashed storage.
18+
*/
19+
class File implements StorageInterface
20+
{
21+
/**
22+
* Name of a storage file.
23+
*
24+
* @var string
25+
*/
26+
private const FILENAME = 'sri-hashes.json';
27+
28+
/**
29+
* @var Filesystem
30+
*/
31+
private Filesystem $filesystem;
32+
33+
/**
34+
* @var LoggerInterface
35+
*/
36+
private LoggerInterface $logger;
37+
38+
/**
39+
* @param Filesystem $filesystem
40+
* @param LoggerInterface $logger
41+
*/
42+
public function __construct(
43+
Filesystem $filesystem,
44+
LoggerInterface $logger
45+
) {
46+
$this->filesystem = $filesystem;
47+
$this->logger = $logger;
48+
}
49+
50+
/**
51+
* @inheritDoc
52+
*/
53+
public function load(?string $context): ?string
54+
{
55+
try {
56+
$staticDir = $this->filesystem->getDirectoryRead(
57+
DirectoryList::STATIC_VIEW
58+
);
59+
60+
$path = $this->resolveFilePath($context);
61+
62+
if (!$staticDir->isFile($path)) {
63+
return null;
64+
}
65+
66+
return $staticDir->readFile($path);
67+
} catch (FileSystemException $exception) {
68+
$this->logger->critical($exception);
69+
70+
return null;
71+
}
72+
}
73+
74+
/**
75+
* @inheritDoc
76+
*/
77+
public function save(string $data, ?string $context): bool
78+
{
79+
try {
80+
$staticDir = $this->filesystem->getDirectoryWrite(
81+
DirectoryList::STATIC_VIEW
82+
);
83+
84+
return (bool) $staticDir->writeFile(
85+
$this->resolveFilePath($context),
86+
$data,
87+
'w'
88+
);
89+
} catch (FileSystemException $exception) {
90+
$this->logger->critical($exception);
91+
92+
return false;
93+
}
94+
}
95+
96+
/**
97+
* @inheritDoc
98+
*/
99+
public function remove(?string $context): bool
100+
{
101+
try {
102+
$staticDir = $this->filesystem->getDirectoryWrite(
103+
DirectoryList::STATIC_VIEW
104+
);
105+
106+
return $staticDir->delete($this->resolveFilePath($context));
107+
} catch (FileSystemException $exception) {
108+
$this->logger->critical($exception);
109+
110+
return false;
111+
}
112+
}
113+
114+
/**
115+
* Resolves a storage file path for a given context.
116+
*
117+
* @param string|null $context
118+
*
119+
* @return string
120+
*/
121+
private function resolveFilePath(?string $context): string
122+
{
123+
return ($context ? $context . DIRECTORY_SEPARATOR : '') . self::FILENAME;
124+
}
125+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Csp\Model\SubresourceIntegrity;
9+
10+
/**
11+
* Interface for an SRI hashes storage.
12+
*/
13+
interface StorageInterface
14+
{
15+
/**
16+
* Loads SRI hashes from a storage.
17+
*
18+
* @param string|null $context
19+
*
20+
* @return string|null
21+
*/
22+
public function load(?string $context): ?string;
23+
24+
/**
25+
* Saves SRI hashes to a storage.
26+
*
27+
* @param string $data
28+
* @param string|null $context
29+
*
30+
* @return bool
31+
*/
32+
public function save(string $data, ?string $context): bool;
33+
34+
/**
35+
* Deletes all SRI hashes from a storage.
36+
*
37+
* @param string|null $context
38+
*
39+
* @return bool
40+
*/
41+
public function remove(?string $context): bool;
42+
}
Lines changed: 39 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

88
namespace Magento\Csp\Model;
99

10+
use Magento\Framework\App\ObjectManager;
1011
use Magento\Framework\App\CacheInterface;
1112
use Magento\Framework\Serialize\SerializerInterface;
13+
use Magento\Csp\Model\SubresourceIntegrity\StorageInterface;
1214

1315
/**
14-
* Class contains methods equivalent to repository design to manage SRI hashes in cache.
16+
* Class contains methods equivalent to repository design to manage SRI hashes.
1517
*/
1618
class SubresourceIntegrityRepository
1719
{
18-
/**
19-
* Cache prefix.
20-
*
21-
* @var string
22-
*/
23-
private const CACHE_PREFIX = 'INTEGRITY';
24-
2520
/**
2621
* @var array|null
2722
*/
@@ -32,37 +27,38 @@ class SubresourceIntegrityRepository
3227
*/
3328
private ?string $context;
3429

35-
/**
36-
* @var CacheInterface
37-
*/
38-
private CacheInterface $cache;
39-
4030
/**
4131
* @var SerializerInterface
4232
*/
4333
private SerializerInterface $serializer;
4434

4535
/**
46-
* @var SubresourceIntegrityFactory
36+
* @var StorageInterface
4737
*/
48-
private SubresourceIntegrityFactory $integrityFactory;
38+
private StorageInterface $storage;
4939

5040
/**
5141
* @param CacheInterface $cache
5242
* @param SerializerInterface $serializer
5343
* @param SubresourceIntegrityFactory $integrityFactory
5444
* @param string|null $context
45+
* @param StorageInterface|null $storage
46+
*
47+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
5548
*/
5649
public function __construct(
5750
CacheInterface $cache,
5851
SerializerInterface $serializer,
5952
SubresourceIntegrityFactory $integrityFactory,
60-
?string $context = null
53+
?string $context = null,
54+
?StorageInterface $storage = null
6155
) {
62-
$this->cache = $cache;
6356
$this->serializer = $serializer;
64-
$this->integrityFactory = $integrityFactory;
6557
$this->context = $context;
58+
59+
$this->storage = $storage ?? ObjectManager::getInstance()->get(
60+
StorageInterface::class
61+
);
6662
}
6763

6864
/**
@@ -74,20 +70,7 @@ public function __construct(
7470
*/
7571
public function getByPath(string $path): ?SubresourceIntegrity
7672
{
77-
$data = $this->getData();
78-
79-
if (isset($data[$path])) {
80-
return $this->integrityFactory->create(
81-
[
82-
"data" => [
83-
"path" => $path,
84-
"hash" => $data[$path]
85-
]
86-
]
87-
);
88-
}
89-
90-
return null;
73+
return $this->getData()[$path] ?? null;
9174
}
9275

9376
/**
@@ -97,20 +80,7 @@ public function getByPath(string $path): ?SubresourceIntegrity
9780
*/
9881
public function getAll(): array
9982
{
100-
$result = [];
101-
102-
foreach ($this->getData() as $path => $hash) {
103-
$result[] = $this->integrityFactory->create(
104-
[
105-
"data" => [
106-
"path" => $path,
107-
"hash" => $hash
108-
]
109-
]
110-
);
111-
}
112-
113-
return $result;
83+
return array_values($this->getData());
11484
}
11585

11686
/**
@@ -124,14 +94,16 @@ public function save(SubresourceIntegrity $integrity): bool
12494
{
12595
$data = $this->getData();
12696

127-
$data[$integrity->getPath()] = $integrity->getHash();
97+
$data[$integrity->getPath()] = $integrity;
12898

12999
$this->data = $data;
130100

131-
return $this->cache->save(
132-
$this->serializer->serialize($this->data),
133-
$this->getCacheKey(),
134-
[self::CACHE_PREFIX]
101+
// Transform the data before saving.
102+
$transformedData = array_map(fn($integrity) => $integrity->getHash(), $this->data);
103+
104+
return $this->storage->save(
105+
$this->serializer->serialize($transformedData),
106+
$this->context
135107
);
136108
}
137109

@@ -147,15 +119,17 @@ public function saveBunch(array $bunch): bool
147119
$data = $this->getData();
148120

149121
foreach ($bunch as $integrity) {
150-
$data[$integrity->getPath()] = $integrity->getHash();
122+
$data[$integrity->getPath()] = $integrity;
151123
}
152124

153125
$this->data = $data;
154126

155-
return $this->cache->save(
156-
$this->serializer->serialize($this->data),
157-
$this->getCacheKey(),
158-
[self::CACHE_PREFIX]
127+
// Transform the data before saving.
128+
$transformedData = array_map(fn($integrity) => $integrity->getHash(), $this->data);
129+
130+
return $this->storage->save(
131+
$this->serializer->serialize($transformedData),
132+
$this->context
159133
);
160134
}
161135

@@ -168,9 +142,7 @@ public function deleteAll(): bool
168142
{
169143
$this->data = null;
170144

171-
return $this->cache->remove(
172-
$this->getCacheKey()
173-
);
145+
return $this->storage->remove($this->context);
174146
}
175147

176148
/**
@@ -181,27 +153,15 @@ public function deleteAll(): bool
181153
private function getData(): array
182154
{
183155
if ($this->data === null) {
184-
$cache = $this->cache->load($this->getCacheKey());
185-
186-
$this->data = $cache ? $this->serializer->unserialize($cache) : [];
187-
}
188-
189-
return $this->data;
190-
}
156+
$rawData = $this->storage->load($this->context);
191157

192-
/**
193-
* Gets a cache key based on current context.
194-
*
195-
* @return string
196-
*/
197-
private function getCacheKey(): string
198-
{
199-
$cacheKey = self::CACHE_PREFIX;
158+
$this->data = $rawData ? $this->serializer->unserialize($rawData) : [];
200159

201-
if ($this->context) {
202-
$cacheKey .= "_" . $this->context;
160+
foreach ($this->data as $path => $hash) {
161+
$this->data[$path] = new SubresourceIntegrity(["path" => $path, "hash" => $hash]);
162+
}
203163
}
204164

205-
return $cacheKey;
165+
return $this->data;
206166
}
207167
}

0 commit comments

Comments
 (0)