Skip to content
This repository was archived by the owner on May 27, 2022. It is now read-only.

Commit 33a74bb

Browse files
authored
Merge pull request #4 from Slamdunk/optional_compression
Split Compression from Encryption to allow encryption-only
2 parents db6310d + 99ad395 commit 33a74bb

8 files changed

+331
-259
lines changed

README.md

+15-5
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,20 @@ $ composer require slam/flysystem-compress-and-encrypt-proxy
2121

2222
```php
2323

24-
use SlamCompressAndEncryptProxy\CompressAndEncryptAdapter;
24+
use SlamCompressAndEncryptProxy\CompressAdapter;
25+
use SlamCompressAndEncryptProxy\EncryptAdapter;
2526
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
2627

2728
// Create a strong key and save it somewhere
28-
$key = EncryptedZipProxyAdapter::generateKey();
29+
$key = EncryptAdapter::generateKey();
2930

3031
// Create the final FilesystemAdapter, for example Aws S3
3132
$remoteAdapter = new AwsS3V3Adapter(/* ... */);
3233

33-
$adapter = new CompressAndEncryptAdapter(
34+
$adapter = new CompressAdapter(new EncryptAdapter(
3435
$remoteAdapter,
3536
$key
36-
);
37+
));
3738

3839
// The FilesystemOperator
3940
$filesystem = new \League\Flysystem\Filesystem($adapter);
@@ -63,16 +64,25 @@ space required.
6364

6465
GZip's `zlib.deflate` and `zlib.inflate` compression filters are used.
6566

67+
You can opt-out compression by using just the `EncryptAdapter`.
68+
6669
## Encryption
6770

6871
[Sodium](https://www.php.net/manual/en/book.sodium.php) extension provides the backend for the
6972
encrypted stream with [`XChaCha20-Poly1305`](https://www.php.net/manual/en/function.sodium-crypto-secretstream-xchacha20poly1305-init-push.php) algorithm.
7073

71-
## MIME types detection caveat
74+
## Caveats
75+
76+
### MIME types detection
7277

7378
Some Flysystem adapters like the Local one try to guess the file mime type by
7479
its nature (content or extension): in such cases it will fail due to the custom
7580
extention and the encrypted content.
7681
Other adapters like the Aws S3 one allow you to specify it manually (for ex.
7782
with the `ContentType` key in the Config): it is a good idea to always manually
7883
inject it, if you like the `Filesystem::mimeType($path)` call to be reliable.
84+
85+
### File size
86+
87+
The file size returned relates to the compressed and encrypted file, not the
88+
original one.

infection.json.dist

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"@default": true,
1616
"global-ignoreSourceCodeByRegex": [
1717
"\\\\assert\\(.+\\);",
18-
"\\$consumed .= .+;"
18+
"\\$consumed ..?= .+;"
1919
],
2020
"FunctionCallRemoval": {
2121
"ignoreSourceCodeByRegex": [

src/AbstractProxyAdapter.php

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SlamCompressAndEncryptProxy;
6+
7+
use League\Flysystem\Config;
8+
use League\Flysystem\FileAttributes;
9+
use League\Flysystem\FilesystemAdapter;
10+
11+
/**
12+
* @internal
13+
*/
14+
abstract class AbstractProxyAdapter implements FilesystemAdapter
15+
{
16+
public function __construct(
17+
private FilesystemAdapter $remoteAdapter
18+
) {
19+
}
20+
21+
abstract public static function getRemoteFileExtension(): string;
22+
23+
/**
24+
* {@inheritDoc}
25+
*/
26+
final public function fileExists(string $path): bool
27+
{
28+
return $this->remoteAdapter->fileExists($this->getRemotePath($path));
29+
}
30+
31+
/**
32+
* {@inheritDoc}
33+
*/
34+
final public function write(string $path, string $contents, Config $config): void
35+
{
36+
$stream = fopen('php://temp', 'w+');
37+
fwrite($stream, $contents);
38+
rewind($stream);
39+
40+
$this->writeStream($path, $stream, $config);
41+
42+
fclose($stream);
43+
}
44+
45+
/**
46+
* {@inheritDoc}
47+
*/
48+
final public function read(string $path): string
49+
{
50+
return stream_get_contents($this->readStream($path));
51+
}
52+
53+
/**
54+
* {@inheritDoc}
55+
*/
56+
final public function delete(string $path): void
57+
{
58+
$this->remoteAdapter->delete($this->getRemotePath($path));
59+
}
60+
61+
/**
62+
* {@inheritDoc}
63+
*/
64+
final public function deleteDirectory(string $path): void
65+
{
66+
$this->remoteAdapter->deleteDirectory($path);
67+
}
68+
69+
/**
70+
* {@inheritDoc}
71+
*/
72+
final public function createDirectory(string $path, Config $config): void
73+
{
74+
$this->remoteAdapter->createDirectory($path, $config);
75+
}
76+
77+
/**
78+
* {@inheritDoc}
79+
*/
80+
final public function setVisibility(string $path, string $visibility): void
81+
{
82+
$this->remoteAdapter->setVisibility($this->getRemotePath($path), $visibility);
83+
}
84+
85+
/**
86+
* {@inheritDoc}
87+
*/
88+
final public function visibility(string $path): FileAttributes
89+
{
90+
return $this->remoteAdapter->visibility($this->getRemotePath($path));
91+
}
92+
93+
/**
94+
* {@inheritDoc}
95+
*/
96+
final public function mimeType(string $path): FileAttributes
97+
{
98+
return $this->remoteAdapter->mimeType($this->getRemotePath($path));
99+
}
100+
101+
/**
102+
* {@inheritDoc}
103+
*/
104+
final public function lastModified(string $path): FileAttributes
105+
{
106+
return $this->remoteAdapter->lastModified($this->getRemotePath($path));
107+
}
108+
109+
/**
110+
* {@inheritDoc}
111+
*/
112+
final public function fileSize(string $path): FileAttributes
113+
{
114+
return $this->remoteAdapter->fileSize($this->getRemotePath($path));
115+
}
116+
117+
/**
118+
* {@inheritDoc}
119+
*/
120+
final public function listContents(string $path, bool $deep): iterable
121+
{
122+
$remoteList = $this->remoteAdapter->listContents($path, $deep);
123+
124+
foreach ($remoteList as $content) {
125+
if ($content instanceof FileAttributes) {
126+
$content = new FileAttributes(
127+
substr($content->path(), 0, -\strlen(static::getRemoteFileExtension())),
128+
$content->fileSize(),
129+
$content->visibility(),
130+
$content->lastModified(),
131+
$content->mimeType(),
132+
$content->extraMetadata()
133+
);
134+
}
135+
136+
yield $content;
137+
}
138+
}
139+
140+
/**
141+
* {@inheritDoc}
142+
*/
143+
final public function move(string $source, string $destination, Config $config): void
144+
{
145+
$this->remoteAdapter->move(
146+
$this->getRemotePath($source),
147+
$this->getRemotePath($destination),
148+
$config
149+
);
150+
}
151+
152+
/**
153+
* {@inheritDoc}
154+
*/
155+
final public function copy(string $source, string $destination, Config $config): void
156+
{
157+
$this->remoteAdapter->copy(
158+
$this->getRemotePath($source),
159+
$this->getRemotePath($destination),
160+
$config
161+
);
162+
}
163+
164+
final protected function getRemoteAdapter(): FilesystemAdapter
165+
{
166+
return $this->remoteAdapter;
167+
}
168+
169+
final protected function getRemotePath(string $path): string
170+
{
171+
return $path.static::getRemoteFileExtension();
172+
}
173+
}

src/CompressAdapter.php

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SlamCompressAndEncryptProxy;
6+
7+
use League\Flysystem\Config;
8+
9+
final class CompressAdapter extends AbstractProxyAdapter
10+
{
11+
public static function getRemoteFileExtension(): string
12+
{
13+
return '.gz';
14+
}
15+
16+
/**
17+
* {@inheritDoc}
18+
*/
19+
public function writeStream(string $path, $contents, Config $config): void
20+
{
21+
$zlibFilter = stream_filter_append($contents, 'zlib.deflate');
22+
\assert(false !== $zlibFilter);
23+
24+
$this->getRemoteAdapter()->writeStream($this->getRemotePath($path), $contents, $config);
25+
}
26+
27+
/**
28+
* {@inheritDoc}
29+
*/
30+
public function readStream(string $path)
31+
{
32+
$contents = $this->getRemoteAdapter()->readStream($this->getRemotePath($path));
33+
34+
$zlibFilter = stream_filter_append($contents, 'zlib.inflate');
35+
\assert(false !== $zlibFilter);
36+
37+
return $contents;
38+
}
39+
}

0 commit comments

Comments
 (0)