3 namespace Friendica\Addon\s3_storage\src;
5 defined('AKEEBAENGINE') or define('AKEEBAENGINE', 1);
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;
15 * The WebDav Backend Storage configuration class
17 class S3Config implements ICanConfigureStorage
19 const NAME = 'S3Config';
21 const DEFAULT_REGION = 'us-east-1';
22 const DEFAULT_ENDPOINT = 's3.amazonaws.com';
23 const DEFAULT_SIGMETHOD = 'v2';
24 const DEFAULT_BUCKET = 'friendica';
29 /** @var IManageConfigValues */
35 /** @var HiddenString */
38 /** @var HiddenString */
42 private $signatureMethod;
56 public function __construct(L10n $l10n, IManageConfigValues $config)
59 $this->config = $config;
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');
72 * Returns the whole configuration as a Akeeba compatible configuration instance
74 * @return Configuration
76 public function getConfig(): Configuration
78 $config = new Configuration($this->accessKey, $this->secretKey);
79 $config->setUseLegacyPathStyle($this->legacy ?? false);
80 $config->setUseDualstackUrl($this->dualStack ?? false);
82 if (!empty($this->region)) {
83 $config->setRegion($this->region);
85 if (!empty($this->endpoint)) {
86 $config->setEndpoint($this->endpoint);
88 if (!empty($this->signatureMethod) && empty($this->endpoint)) {
89 $config->setSignatureMethod($this->signatureMethod);
95 public function getBucket(): string
103 public function getOptions(): array
113 $this->l10n->t('Access Key'),
115 $this->l10n->t('Set the Access Key of the S3 storage'),
120 $this->l10n->t('Secret Key'),
122 $this->l10n->t('Set the Secret Key of the S3 storage'),
127 $this->l10n->t('Bucket'),
129 $this->l10n->t('The S3 Bucket (name), you want to use with Friendica'),
132 'signature_method' => [
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)'),
141 $this->l10n->t("Amazon S3 compatible endpoint (leave empty for '%s')", self::DEFAULT_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.)'),
147 $this->l10n->t("AWS region (leave empty for '%s')", self::DEFAULT_REGION),
149 $this->l10n->t('The AWS region is mandatory for v4 signatures'),
153 $this->l10n->t('Use the dualstack URL (which will ship traffic over ipv6 in most cases)'),
155 $this->l10n->t('For more information on these endpoints please read https://docs.aws.amazon.com/AmazonS3/latest/dev/dual-stack-endpoints.html'),
159 $this->l10n->t('Use legacy, path-style access to the bucket'),
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'),
169 public function saveOptions(array $data): array
173 if (empty($data['access_key']) || empty($data['secret_key']) || empty($data['bucket'])) {
175 'access_key' => $this->l10n->t('Invalid input')
179 $s3Config = new Configuration(
184 $bucket = $data['bucket'];
186 if (!empty($data['endpoint'])) {
188 $s3Config->setEndpoint($data['endpoint']);
189 } catch (\Exception $exception) {
190 $feedback['endpoint'] = $exception->getMessage();
193 if (!empty($data['region'])) {
195 $s3Config->setRegion($data['region']);
196 } catch (\Exception $exception) {
197 $feedback['region'] = $exception->getMessage();
202 $s3Config->setUseLegacyPathStyle(!empty($data['legacy']));
203 } catch (\Exception $exception) {
204 $feedback['legacy'] = $exception->getMessage();
207 $s3Config->setUseDualstackUrl(!empty($data['dualstack_url']));
208 } catch (\Exception $exception) {
209 $feedback['dualstack_url'] = $exception->getMessage();
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());
218 $connector = new Connector($s3Config);
219 $buckets = $connector->listBuckets();
220 if (!in_array($bucket, $buckets)) {
222 'bucket' => $this->l10n->t('Bucket %s cannot be not found, possible buckets: %s', $bucket, implode(', ', $buckets))
225 $connector->getBucket($bucket);
226 } catch (\Exception $exception) {
228 'bucket' => $exception->getMessage()
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));
236 $this->config->set('s3', 'legacy', $s3Config->getUseLegacyPathStyle());
237 $this->config->set('s3', 'dual_stack', $s3Config->getUseLegacyPathStyle());
238 $this->config->set('s3','signature_method', $s3Config->getSignatureMethod());
240 if (!empty($data['endpoint'])) {
241 $this->config->set('s3', 'endpoint', $s3Config->getEndpoint());
243 $this->config->delete('s3', 'endpoint');
246 if (!empty($data['region'])) {
247 $this->config->set('s3', 'region', $s3Config->getRegion());
249 $this->config->delete('s3', 'region');