]> git.mxchange.org Git - friendica-addons.git/blob - s3_storage/src/S3Config.php
Config fix
[friendica-addons.git] / s3_storage / src / S3Config.php
1 <?php
2
3 namespace Friendica\Addon\s3_storage\src;
4
5 defined('AKEEBAENGINE') or define('AKEEBAENGINE', 1);
6
7 use Akeeba\Engine\Postproc\Connector\S3v4\Configuration;
8 use Akeeba\Engine\Postproc\Connector\S3v4\Connector;
9 use Friendica\Core\Config\Capability\IManageConfigValues;
10 use Friendica\Core\L10n;
11 use Friendica\Core\Storage\Capability\ICanConfigureStorage;
12 use ParagonIE\HiddenString\HiddenString;
13
14 /**
15  * The WebDav Backend Storage configuration class
16  */
17 class S3Config implements ICanConfigureStorage
18 {
19         const NAME = 'S3Config';
20
21         const DEFAULT_REGION    = 'us-east-1';
22         const DEFAULT_ENDPOINT  = 's3.amazonaws.com';
23         const DEFAULT_SIGMETHOD = 'v2';
24         const DEFAULT_BUCKET    = 'friendica';
25
26         /** @var L10n */
27         private $l10n;
28
29         /** @var IManageConfigValues */
30         private $config;
31
32         /** @var string */
33         private $endpoint;
34
35         /** @var HiddenString */
36         private $accessKey;
37
38         /** @var HiddenString */
39         private $secretKey;
40
41         /** @var string */
42         private $signatureMethod;
43
44         /** @var ?string */
45         private $bucket;
46
47         /** @var ?string */
48         private $region;
49
50         /** @var bool */
51         private $legacy;
52
53         /** @var bool */
54         private $dualStack;
55
56         public function __construct(L10n $l10n, IManageConfigValues $config)
57         {
58                 $this->l10n   = $l10n;
59                 $this->config = $config;
60
61                 $this->accessKey       = new HiddenString($this->config->get('s3', 'access_key', ''));
62                 $this->secretKey       = new HiddenString($this->config->get('s3', 'secret_key', ''));
63                 $this->signatureMethod = $this->config->get('s3', 'signature_method', self::DEFAULT_SIGMETHOD);
64                 $this->bucket          = $this->config->get('s3', 'bucket', self::DEFAULT_BUCKET);
65                 $this->legacy          = !empty($this->config->get('s3', 'legacy'));
66                 $this->dualStack       = !empty($this->config->get('s3', 'dual_stack'));
67                 $this->region          = $this->config->get('s3', 'region');
68                 $this->endpoint        = $this->config->get('s3', 'endpoint');
69         }
70
71         /**
72          * Returns the whole configuration as a Akeeba compatible configuration instance
73          *
74          * @return Configuration
75          */
76         public function getConfig(): Configuration
77         {
78                 $config = new Configuration($this->accessKey, $this->secretKey);
79                 $config->setUseLegacyPathStyle($this->legacy ?? false);
80                 $config->setUseDualstackUrl($this->dualStack ?? false);
81
82                 if (!empty($this->region)) {
83                         $config->setRegion($this->region);
84                 }
85                 if (!empty($this->endpoint)) {
86                         $config->setEndpoint($this->endpoint);
87                 }
88                 if (!empty($this->signatureMethod) && empty($this->endpoint)) {
89                         $config->setSignatureMethod($this->signatureMethod);
90                 }
91
92                 return $config;
93         }
94
95         public function getBucket(): string
96         {
97                 return $this->bucket;
98         }
99
100         /**
101          * @inheritDoc
102          */
103         public function getOptions(): array
104         {
105                 $sigMethods = [
106                         'v2' => 'v2',
107                         'v4' => 'v4',
108                 ];
109
110                 return [
111                         'access_key' => [
112                                 'password',
113                                 $this->l10n->t('Access Key'),
114                                 $this->accessKey,
115                                 $this->l10n->t('Set the Access Key of the S3 storage'),
116                                 true,
117                         ],
118                         'secret_key' => [
119                                 'password',
120                                 $this->l10n->t('Secret Key'),
121                                 $this->secretKey,
122                                 $this->l10n->t('Set the Secret Key of the S3 storage'),
123                                 true,
124                         ],
125                         'bucket' => [
126                                 'input',
127                                 $this->l10n->t('Bucket'),
128                                 $this->bucket,
129                                 $this->l10n->t('The S3 Bucket (name), you want to use with Friendica'),
130                                 true,
131                         ],
132                         'signature_method' => [
133                                 'select',
134                                 $this->l10n->t('Signature Method'),
135                                 $this->signatureMethod,
136                                 $this->l10n->t('Set the signature method to use (BEWARE: v4 will be automatically set to v2 in case the endpoint isn\'t amazon)'),
137                                 $sigMethods,
138                         ],
139                         'endpoint' => [
140                                 'input',
141                                 $this->l10n->t("Amazon S3 compatible endpoint (leave empty for '%s')", self::DEFAULT_ENDPOINT),
142                                 $this->endpoint,
143                                 $this->l10n->t('Set the S3 endpoint. Do NOT use a protocol (You can use a custom endpoint with v2 signatures to access third party services which offer S3 compatibility, e.g. OwnCloud, Google Storage etc.)'),
144                         ],
145                         'region' => [
146                                 'input',
147                                 $this->l10n->t("AWS region (leave empty for '%s')", self::DEFAULT_REGION),
148                                 $this->region,
149                                 $this->l10n->t('The AWS region is mandatory for v4 signatures'),
150                         ],
151                         'dualstack_url' => [
152                                 'checkbox',
153                                 $this->l10n->t('Use the dualstack URL (which will ship traffic over ipv6 in most cases)'),
154                                 $this->dualStack,
155                                 $this->l10n->t('For more information on these endpoints please read https://docs.aws.amazon.com/AmazonS3/latest/dev/dual-stack-endpoints.html'),
156                         ],
157                         'legacy' => [
158                                 'checkbox',
159                                 $this->l10n->t('Use legacy, path-style access to the bucket'),
160                                 $this->legacy,
161                                 $this->l10n->t('When it\'s turned off (default) we use virtual hosting stylepaths which are RECOMMENDED BY AMAZON per http://docs.aws.amazon.com/AmazonS3/latest/API/APIRest.html'),
162                         ],
163                 ];
164         }
165
166         /**
167          * @inheritDoc
168          */
169         public function saveOptions(array $data): array
170         {
171                 $feedback = [];
172
173                 if (empty($data['access_key']) || empty($data['secret_key']) || empty($data['bucket'])) {
174                         return [
175                                 'access_key' => $this->l10n->t('Invalid input')
176                         ];
177                 }
178
179                 $s3Config = new Configuration(
180                         $data['access_key'],
181                         $data['secret_key']
182                 );
183
184                 $bucket = $data['bucket'];
185
186                 if (!empty($data['endpoint'])) {
187                         try {
188                                 $s3Config->setEndpoint($data['endpoint']);
189                         } catch (\Exception $exception) {
190                                 $feedback['endpoint'] = $exception->getMessage();
191                         }
192                 }
193                 if (!empty($data['region'])) {
194                         try {
195                                 $s3Config->setRegion($data['region']);
196                         } catch (\Exception $exception) {
197                                 $feedback['region'] = $exception->getMessage();
198                         }
199                 }
200
201                 try {
202                         $s3Config->setUseLegacyPathStyle((bool)$data['legacy'] ?? false);
203                 } catch (\Exception $exception) {
204                         $feedback['legacy'] = $exception->getMessage();
205                 }
206                 try {
207                         $s3Config->setUseDualstackUrl((bool)$data['dualstack_url'] ?? false);
208                 } catch (\Exception $exception) {
209                         $feedback['dualstack_url'] = $exception->getMessage();
210                 }
211                 try {
212                         $s3Config->setSignatureMethod($data['signature_method'] ?? self::DEFAULT_SIGMETHOD);
213                 } catch (\Exception $exception) {
214                         $feedback['signature_method'] = $this->l10n->t("error '%s', message '%s'", $exception->getCode(), $exception->getMessage());
215                 }
216
217                 try {
218                         $connector = new Connector($s3Config);
219                         $buckets   = $connector->listBuckets();
220                         if (!in_array($bucket, $buckets)) {
221                                 return [
222                                         'bucket' => $this->l10n->t('Bucket %s cannot be not found, possible buckets: %s', $bucket, implode(', ', $buckets))
223                                 ];
224                         }
225                         $connector->getBucket($bucket);
226                 } catch (\Exception $exception) {
227                         return [
228                                 'bucket' => $exception->getMessage()
229                         ];
230                 }
231
232                 $this->config->set('s3', 'access_key', ($this->accessKey = new HiddenString($data['access_key']))->getString());
233                 $this->config->set('s3', 'secret_key', ($this->secretKey = new HiddenString($data['secret_key']))->getString());
234                 $this->config->set('s3', 'bucket', ($this->bucket = $bucket));
235
236                 if ($s3Config->getUseLegacyPathStyle()) {
237                         $this->config->set('s3', 'legacy', '1');
238                 } else {
239                         $this->config->delete('s3', 'legacy');
240                 }
241                 if ($s3Config->getDualstackUrl()) {
242                         $this->config->set('s3', 'dual_stack', '1');
243                 } else {
244                         $this->config->delete('s3', 'dual_stack');
245                 }
246                 $this->config->set('s3','signature_method', $s3Config->getSignatureMethod());
247
248                 if (!empty($data['endpoint'])) {
249                         $this->config->set('s3', 'endpoint', $s3Config->getEndpoint());
250                 } else {
251                         $this->config->delete('s3', 'endpoint');
252                 }
253
254                 if (!empty($data['region'])) {
255                         $this->config->set('s3', 'region', $s3Config->getRegion());
256                 } else {
257                         $this->config->delete('s3', 'region');
258                 }
259
260                 return $feedback;
261         }
262 }