Passed
Push — master ( 987f62...6e4b08 )
by Roeland
13:22 queued 10s
created

S3ConnectionTrait::parseParams()   B

Complexity

Conditions 10
Paths 81

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 12
c 1
b 0
f 0
nc 81
nop 1
dl 0
loc 17
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Robin Appelman <[email protected]>
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Christoph Wurst <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author S. Cat <[email protected]>
10
 * @author Stephen Cuppett <[email protected]>
11
 *
12
 * @license GNU AGPL version 3 or any later version
13
 *
14
 * This program is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License as
16
 * published by the Free Software Foundation, either version 3 of the
17
 * License, or (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License
25
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26
 *
27
 */
28
29
namespace OC\Files\ObjectStore;
30
31
use Aws\ClientResolver;
32
use Aws\Credentials\CredentialProvider;
33
use Aws\Credentials\Credentials;
34
use Aws\Exception\CredentialsException;
35
use Aws\S3\Exception\S3Exception;
36
use Aws\S3\S3Client;
37
use GuzzleHttp\Promise;
38
use GuzzleHttp\Promise\RejectedPromise;
39
use OCP\ILogger;
40
41
trait S3ConnectionTrait {
42
	/** @var array */
43
	protected $params;
44
45
	/** @var S3Client */
46
	protected $connection;
47
48
	/** @var string */
49
	protected $id;
50
51
	/** @var string */
52
	protected $bucket;
53
54
	/** @var int */
55
	protected $timeout;
56
57
	/** @var int */
58
	protected $uploadPartSize;
59
60
	protected $test;
61
62
	protected function parseParams($params) {
63
		if (empty($params['bucket'])) {
64
			throw new \Exception("Bucket has to be configured.");
65
		}
66
67
		$this->id = 'amazon::' . $params['bucket'];
68
69
		$this->test = isset($params['test']);
70
		$this->bucket = $params['bucket'];
71
		$this->timeout = !isset($params['timeout']) ? 15 : $params['timeout'];
72
		$this->uploadPartSize = !isset($params['uploadPartSize']) ? 524288000 : $params['uploadPartSize'];
73
		$params['region'] = empty($params['region']) ? 'eu-west-1' : $params['region'];
74
		$params['hostname'] = empty($params['hostname']) ? 's3.' . $params['region'] . '.amazonaws.com' : $params['hostname'];
75
		if (!isset($params['port']) || $params['port'] === '') {
76
			$params['port'] = (isset($params['use_ssl']) && $params['use_ssl'] === false) ? 80 : 443;
77
		}
78
		$this->params = $params;
79
	}
80
81
	public function getBucket() {
82
		return $this->bucket;
83
	}
84
85
	/**
86
	 * Returns the connection
87
	 *
88
	 * @return S3Client connected client
89
	 * @throws \Exception if connection could not be made
90
	 */
91
	public function getConnection() {
92
		if (!is_null($this->connection)) {
93
			return $this->connection;
94
		}
95
96
		$scheme = (isset($this->params['use_ssl']) && $this->params['use_ssl'] === false) ? 'http' : 'https';
97
		$base_url = $scheme . '://' . $this->params['hostname'] . ':' . $this->params['port'] . '/';
98
99
		// Adding explicit credential provider to the beginning chain.
100
		// Including environment variables and IAM instance profiles.
101
		$provider = CredentialProvider::memoize(
102
			CredentialProvider::chain(
103
				$this->paramCredentialProvider(),
104
				CredentialProvider::env(),
105
				CredentialProvider::instanceProfile()
106
			)
107
		);
108
109
		$options = [
110
			'version' => isset($this->params['version']) ? $this->params['version'] : 'latest',
111
			'credentials' => $provider,
112
			'endpoint' => $base_url,
113
			'region' => $this->params['region'],
114
			'use_path_style_endpoint' => isset($this->params['use_path_style']) ? $this->params['use_path_style'] : false,
115
			'signature_provider' => \Aws\or_chain([self::class, 'legacySignatureProvider'], ClientResolver::_default_signature_provider()),
116
			'csm' => false,
117
		];
118
		if (isset($this->params['proxy'])) {
119
			$options['request.options'] = ['proxy' => $this->params['proxy']];
120
		}
121
		if (isset($this->params['legacy_auth']) && $this->params['legacy_auth']) {
122
			$options['signature_version'] = 'v2';
123
		}
124
		$this->connection = new S3Client($options);
125
126
		if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
127
			$logger = \OC::$server->getLogger();
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getLogger() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

127
			$logger = /** @scrutinizer ignore-deprecated */ \OC::$server->getLogger();
Loading history...
128
			$logger->debug('Bucket "' . $this->bucket . '" This bucket name is not dns compatible, it may contain invalid characters.',
129
					 ['app' => 'objectstore']);
130
		}
131
132
		if (!$this->connection->doesBucketExist($this->bucket)) {
133
			$logger = \OC::$server->getLogger();
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getLogger() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

133
			$logger = /** @scrutinizer ignore-deprecated */ \OC::$server->getLogger();
Loading history...
134
			try {
135
				$logger->info('Bucket "' . $this->bucket . '" does not exist - creating it.', ['app' => 'objectstore']);
136
				if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
137
					throw new \Exception("The bucket will not be created because the name is not dns compatible, please correct it: " . $this->bucket);
138
				}
139
				$this->connection->createBucket(['Bucket' => $this->bucket]);
140
				$this->testTimeout();
141
			} catch (S3Exception $e) {
142
				$logger->logException($e, [
143
					'message' => 'Invalid remote storage.',
144
					'level' => ILogger::DEBUG,
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::DEBUG has been deprecated: 20.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

144
					'level' => /** @scrutinizer ignore-deprecated */ ILogger::DEBUG,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
145
					'app' => 'objectstore',
146
				]);
147
				throw new \Exception('Creation of bucket "' . $this->bucket . '" failed. ' . $e->getMessage());
148
			}
149
		}
150
151
		// google cloud's s3 compatibility doesn't like the EncodingType parameter
152
		if (strpos($base_url, 'storage.googleapis.com')) {
153
			$this->connection->getHandlerList()->remove('s3.auto_encode');
154
		}
155
156
		return $this->connection;
157
	}
158
159
	/**
160
	 * when running the tests wait to let the buckets catch up
161
	 */
162
	private function testTimeout() {
163
		if ($this->test) {
164
			sleep($this->timeout);
165
		}
166
	}
167
168
	public static function legacySignatureProvider($version, $service, $region) {
0 ignored issues
show
Unused Code introduced by
The parameter $region is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

168
	public static function legacySignatureProvider($version, $service, /** @scrutinizer ignore-unused */ $region) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $service is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

168
	public static function legacySignatureProvider($version, /** @scrutinizer ignore-unused */ $service, $region) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
169
		switch ($version) {
170
			case 'v2':
171
			case 's3':
172
				return new S3Signature();
173
			default:
174
				return null;
175
		}
176
	}
177
178
	/**
179
	 * This function creates a credential provider based on user parameter file
180
	 */
181
	protected function paramCredentialProvider() : callable {
182
		return function () {
183
			$key = empty($this->params['key']) ? null : $this->params['key'];
184
			$secret = empty($this->params['secret']) ? null : $this->params['secret'];
185
186
			if ($key && $secret) {
187
				return Promise\promise_for(
0 ignored issues
show
Bug introduced by
The function promise_for was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

187
				return /** @scrutinizer ignore-call */ Promise\promise_for(
Loading history...
188
					new Credentials($key, $secret)
189
				);
190
			}
191
192
			$msg = 'Could not find parameters set for credentials in config file.';
193
			return new RejectedPromise(new CredentialsException($msg));
194
		};
195
	}
196
}
197