Passed
Push — master ( c709dd...873be1 )
by Roeland
38:51 queued 28:30
created
lib/private/Files/ObjectStore/S3Signature.php 1 patch
Indentation   +184 added lines, -184 removed lines patch added patch discarded remove patch
@@ -34,188 +34,188 @@
 block discarded – undo
34 34
  * Legacy Amazon S3 signature implementation
35 35
  */
36 36
 class S3Signature implements SignatureInterface {
37
-	/** @var array Query string values that must be signed */
38
-	private $signableQueryString = [
39
-		'acl', 'cors', 'delete', 'lifecycle', 'location', 'logging',
40
-		'notification', 'partNumber', 'policy', 'requestPayment',
41
-		'response-cache-control', 'response-content-disposition',
42
-		'response-content-encoding', 'response-content-language',
43
-		'response-content-type', 'response-expires', 'restore', 'tagging',
44
-		'torrent', 'uploadId', 'uploads', 'versionId', 'versioning',
45
-		'versions', 'website'
46
-	];
47
-
48
-	/** @var array Sorted headers that must be signed */
49
-	private $signableHeaders = ['Content-MD5', 'Content-Type'];
50
-
51
-	/** @var \Aws\S3\S3UriParser S3 URI parser */
52
-	private $parser;
53
-
54
-	public function __construct() {
55
-		$this->parser = new S3UriParser();
56
-		// Ensure that the signable query string parameters are sorted
57
-		sort($this->signableQueryString);
58
-	}
59
-
60
-	public function signRequest(
61
-		RequestInterface $request,
62
-		CredentialsInterface $credentials
63
-	) {
64
-		$request = $this->prepareRequest($request, $credentials);
65
-		$stringToSign = $this->createCanonicalizedString($request);
66
-		$auth = 'AWS '
67
-			. $credentials->getAccessKeyId() . ':'
68
-			. $this->signString($stringToSign, $credentials);
69
-
70
-		return $request->withHeader('Authorization', $auth);
71
-	}
72
-
73
-	public function presign(
74
-		RequestInterface $request,
75
-		CredentialsInterface $credentials,
76
-		$expires,
77
-		array $options = []
78
-	) {
79
-		$query = [];
80
-		// URL encoding already occurs in the URI template expansion. Undo that
81
-		// and encode using the same encoding as GET object, PUT object, etc.
82
-		$uri = $request->getUri();
83
-		$path = S3Client::encodeKey(rawurldecode($uri->getPath()));
84
-		$request = $request->withUri($uri->withPath($path));
85
-
86
-		// Make sure to handle temporary credentials
87
-		if ($token = $credentials->getSecurityToken()) {
88
-			$request = $request->withHeader('X-Amz-Security-Token', $token);
89
-			$query['X-Amz-Security-Token'] = $token;
90
-		}
91
-
92
-		if ($expires instanceof \DateTime) {
93
-			$expires = $expires->getTimestamp();
94
-		} elseif (!is_numeric($expires)) {
95
-			$expires = strtotime($expires);
96
-		}
97
-
98
-		// Set query params required for pre-signed URLs
99
-		$query['AWSAccessKeyId'] = $credentials->getAccessKeyId();
100
-		$query['Expires'] = $expires;
101
-		$query['Signature'] = $this->signString(
102
-			$this->createCanonicalizedString($request, $expires),
103
-			$credentials
104
-		);
105
-
106
-		// Move X-Amz-* headers to the query string
107
-		foreach ($request->getHeaders() as $name => $header) {
108
-			$name = strtolower($name);
109
-			if (strpos($name, 'x-amz-') === 0) {
110
-				$query[$name] = implode(',', $header);
111
-			}
112
-		}
113
-
114
-		$queryString = http_build_query($query, null, '&', PHP_QUERY_RFC3986);
115
-
116
-		return $request->withUri($request->getUri()->withQuery($queryString));
117
-	}
118
-
119
-	/**
120
-	 * @param RequestInterface     $request
121
-	 * @param CredentialsInterface $creds
122
-	 *
123
-	 * @return RequestInterface
124
-	 */
125
-	private function prepareRequest(
126
-		RequestInterface $request,
127
-		CredentialsInterface $creds
128
-	) {
129
-		$modify = [
130
-			'remove_headers' => ['X-Amz-Date'],
131
-			'set_headers'    => ['Date' => gmdate(\DateTime::RFC2822)]
132
-		];
133
-
134
-		// Add the security token header if one is being used by the credentials
135
-		if ($token = $creds->getSecurityToken()) {
136
-			$modify['set_headers']['X-Amz-Security-Token'] = $token;
137
-		}
138
-
139
-		return Psr7\modify_request($request, $modify);
140
-	}
141
-
142
-	private function signString($string, CredentialsInterface $credentials) {
143
-		return base64_encode(
144
-			hash_hmac('sha1', $string, $credentials->getSecretKey(), true)
145
-		);
146
-	}
147
-
148
-	private function createCanonicalizedString(
149
-		RequestInterface $request,
150
-		$expires = null
151
-	) {
152
-		$buffer = $request->getMethod() . "\n";
153
-
154
-		// Add the interesting headers
155
-		foreach ($this->signableHeaders as $header) {
156
-			$buffer .= $request->getHeaderLine($header) . "\n";
157
-		}
158
-
159
-		$date = $expires ?: $request->getHeaderLine('date');
160
-		$buffer .= "{$date}\n"
161
-			. $this->createCanonicalizedAmzHeaders($request)
162
-			. $this->createCanonicalizedResource($request);
163
-
164
-		return $buffer;
165
-	}
166
-
167
-	private function createCanonicalizedAmzHeaders(RequestInterface $request) {
168
-		$headers = [];
169
-		foreach ($request->getHeaders() as $name => $header) {
170
-			$name = strtolower($name);
171
-			if (strpos($name, 'x-amz-') === 0) {
172
-				$value = implode(',', $header);
173
-				if (strlen($value) > 0) {
174
-					$headers[$name] = $name . ':' . $value;
175
-				}
176
-			}
177
-		}
178
-
179
-		if (!$headers) {
180
-			return '';
181
-		}
182
-
183
-		ksort($headers);
184
-
185
-		return implode("\n", $headers) . "\n";
186
-	}
187
-
188
-	private function createCanonicalizedResource(RequestInterface $request) {
189
-		$data = $this->parser->parse($request->getUri());
190
-		$buffer = '/';
191
-
192
-		if ($data['bucket']) {
193
-			$buffer .= $data['bucket'];
194
-			if (!empty($data['key']) || !$data['path_style']) {
195
-				$buffer .= '/' . $data['key'];
196
-			}
197
-		}
198
-
199
-		// Add sub resource parameters if present.
200
-		$query = $request->getUri()->getQuery();
201
-
202
-		if ($query) {
203
-			$params = Psr7\parse_query($query);
204
-			$first = true;
205
-			foreach ($this->signableQueryString as $key) {
206
-				if (array_key_exists($key, $params)) {
207
-					$value = $params[$key];
208
-					$buffer .= $first ? '?' : '&';
209
-					$first = false;
210
-					$buffer .= $key;
211
-					// Don't add values for empty sub-resources
212
-					if (strlen($value)) {
213
-						$buffer .= "={$value}";
214
-					}
215
-				}
216
-			}
217
-		}
218
-
219
-		return $buffer;
220
-	}
37
+    /** @var array Query string values that must be signed */
38
+    private $signableQueryString = [
39
+        'acl', 'cors', 'delete', 'lifecycle', 'location', 'logging',
40
+        'notification', 'partNumber', 'policy', 'requestPayment',
41
+        'response-cache-control', 'response-content-disposition',
42
+        'response-content-encoding', 'response-content-language',
43
+        'response-content-type', 'response-expires', 'restore', 'tagging',
44
+        'torrent', 'uploadId', 'uploads', 'versionId', 'versioning',
45
+        'versions', 'website'
46
+    ];
47
+
48
+    /** @var array Sorted headers that must be signed */
49
+    private $signableHeaders = ['Content-MD5', 'Content-Type'];
50
+
51
+    /** @var \Aws\S3\S3UriParser S3 URI parser */
52
+    private $parser;
53
+
54
+    public function __construct() {
55
+        $this->parser = new S3UriParser();
56
+        // Ensure that the signable query string parameters are sorted
57
+        sort($this->signableQueryString);
58
+    }
59
+
60
+    public function signRequest(
61
+        RequestInterface $request,
62
+        CredentialsInterface $credentials
63
+    ) {
64
+        $request = $this->prepareRequest($request, $credentials);
65
+        $stringToSign = $this->createCanonicalizedString($request);
66
+        $auth = 'AWS '
67
+            . $credentials->getAccessKeyId() . ':'
68
+            . $this->signString($stringToSign, $credentials);
69
+
70
+        return $request->withHeader('Authorization', $auth);
71
+    }
72
+
73
+    public function presign(
74
+        RequestInterface $request,
75
+        CredentialsInterface $credentials,
76
+        $expires,
77
+        array $options = []
78
+    ) {
79
+        $query = [];
80
+        // URL encoding already occurs in the URI template expansion. Undo that
81
+        // and encode using the same encoding as GET object, PUT object, etc.
82
+        $uri = $request->getUri();
83
+        $path = S3Client::encodeKey(rawurldecode($uri->getPath()));
84
+        $request = $request->withUri($uri->withPath($path));
85
+
86
+        // Make sure to handle temporary credentials
87
+        if ($token = $credentials->getSecurityToken()) {
88
+            $request = $request->withHeader('X-Amz-Security-Token', $token);
89
+            $query['X-Amz-Security-Token'] = $token;
90
+        }
91
+
92
+        if ($expires instanceof \DateTime) {
93
+            $expires = $expires->getTimestamp();
94
+        } elseif (!is_numeric($expires)) {
95
+            $expires = strtotime($expires);
96
+        }
97
+
98
+        // Set query params required for pre-signed URLs
99
+        $query['AWSAccessKeyId'] = $credentials->getAccessKeyId();
100
+        $query['Expires'] = $expires;
101
+        $query['Signature'] = $this->signString(
102
+            $this->createCanonicalizedString($request, $expires),
103
+            $credentials
104
+        );
105
+
106
+        // Move X-Amz-* headers to the query string
107
+        foreach ($request->getHeaders() as $name => $header) {
108
+            $name = strtolower($name);
109
+            if (strpos($name, 'x-amz-') === 0) {
110
+                $query[$name] = implode(',', $header);
111
+            }
112
+        }
113
+
114
+        $queryString = http_build_query($query, null, '&', PHP_QUERY_RFC3986);
115
+
116
+        return $request->withUri($request->getUri()->withQuery($queryString));
117
+    }
118
+
119
+    /**
120
+     * @param RequestInterface     $request
121
+     * @param CredentialsInterface $creds
122
+     *
123
+     * @return RequestInterface
124
+     */
125
+    private function prepareRequest(
126
+        RequestInterface $request,
127
+        CredentialsInterface $creds
128
+    ) {
129
+        $modify = [
130
+            'remove_headers' => ['X-Amz-Date'],
131
+            'set_headers'    => ['Date' => gmdate(\DateTime::RFC2822)]
132
+        ];
133
+
134
+        // Add the security token header if one is being used by the credentials
135
+        if ($token = $creds->getSecurityToken()) {
136
+            $modify['set_headers']['X-Amz-Security-Token'] = $token;
137
+        }
138
+
139
+        return Psr7\modify_request($request, $modify);
140
+    }
141
+
142
+    private function signString($string, CredentialsInterface $credentials) {
143
+        return base64_encode(
144
+            hash_hmac('sha1', $string, $credentials->getSecretKey(), true)
145
+        );
146
+    }
147
+
148
+    private function createCanonicalizedString(
149
+        RequestInterface $request,
150
+        $expires = null
151
+    ) {
152
+        $buffer = $request->getMethod() . "\n";
153
+
154
+        // Add the interesting headers
155
+        foreach ($this->signableHeaders as $header) {
156
+            $buffer .= $request->getHeaderLine($header) . "\n";
157
+        }
158
+
159
+        $date = $expires ?: $request->getHeaderLine('date');
160
+        $buffer .= "{$date}\n"
161
+            . $this->createCanonicalizedAmzHeaders($request)
162
+            . $this->createCanonicalizedResource($request);
163
+
164
+        return $buffer;
165
+    }
166
+
167
+    private function createCanonicalizedAmzHeaders(RequestInterface $request) {
168
+        $headers = [];
169
+        foreach ($request->getHeaders() as $name => $header) {
170
+            $name = strtolower($name);
171
+            if (strpos($name, 'x-amz-') === 0) {
172
+                $value = implode(',', $header);
173
+                if (strlen($value) > 0) {
174
+                    $headers[$name] = $name . ':' . $value;
175
+                }
176
+            }
177
+        }
178
+
179
+        if (!$headers) {
180
+            return '';
181
+        }
182
+
183
+        ksort($headers);
184
+
185
+        return implode("\n", $headers) . "\n";
186
+    }
187
+
188
+    private function createCanonicalizedResource(RequestInterface $request) {
189
+        $data = $this->parser->parse($request->getUri());
190
+        $buffer = '/';
191
+
192
+        if ($data['bucket']) {
193
+            $buffer .= $data['bucket'];
194
+            if (!empty($data['key']) || !$data['path_style']) {
195
+                $buffer .= '/' . $data['key'];
196
+            }
197
+        }
198
+
199
+        // Add sub resource parameters if present.
200
+        $query = $request->getUri()->getQuery();
201
+
202
+        if ($query) {
203
+            $params = Psr7\parse_query($query);
204
+            $first = true;
205
+            foreach ($this->signableQueryString as $key) {
206
+                if (array_key_exists($key, $params)) {
207
+                    $value = $params[$key];
208
+                    $buffer .= $first ? '?' : '&';
209
+                    $first = false;
210
+                    $buffer .= $key;
211
+                    // Don't add values for empty sub-resources
212
+                    if (strlen($value)) {
213
+                        $buffer .= "={$value}";
214
+                    }
215
+                }
216
+            }
217
+        }
218
+
219
+        return $buffer;
220
+    }
221 221
 }
Please login to merge, or discard this patch.