Auth   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 276
Duplicated Lines 0 %

Test Coverage

Coverage 59.78%

Importance

Changes 5
Bugs 1 Features 3
Metric Value
eloc 153
c 5
b 1
f 3
dl 0
loc 276
rs 8.48
ccs 55
cts 92
cp 0.5978
wmc 49

12 Methods

Rating   Name   Duplication   Size   Complexity  
F signQiniuAuthorization() 0 67 19
A privateDownloadUrl() 0 14 2
A signRequest() 0 16 5
A getAccessKey() 0 3 1
A sign() 0 4 1
A signWithData() 0 4 1
A verifyCallback() 0 27 4
A uploadToken() 0 14 2
A __construct() 0 11 2
A authorization() 0 4 1
B authorizationV2() 0 28 6
A copyPolicy() 0 11 5

How to fix   Complexity   

Complex Class

Complex classes like Auth often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Auth, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Qiniu;
4
5
use Qiniu\Http\Header;
6
use Qiniu\Zone;
7
8
final class Auth
9
{
10
    private $accessKey;
11
    private $secretKey;
12
    public $options;
13
14
    public function __construct($accessKey, $secretKey, $options = null)
15
    {
16
        $this->accessKey = $accessKey;
17 6
        $this->secretKey = $secretKey;
18
        $defaultOptions = array(
19 6
            'disableQiniuTimestampSignature' => null
20
        );
21
        if ($options == null) {
22 93
            $options = $defaultOptions;
23
        }
24 93
        $this->options = array_merge($defaultOptions, $options);
25 93
    }
26
27
    public function getAccessKey()
28 24
    {
29
        return $this->accessKey;
30 24
    }
31 24
32
    public function sign($data)
33
    {
34 54
        $hmac = hash_hmac('sha1', $data, $this->secretKey, true);
35
        return $this->accessKey . ':' . \Qiniu\base64_urlSafeEncode($hmac);
36 54
    }
37 54
38 54
    public function signWithData($data)
39 51
    {
40 51
        $encodedData = \Qiniu\base64_urlSafeEncode($data);
41 54
        return $this->sign($encodedData) . ':' . $encodedData;
42 9
    }
43 9
44 54
    public function signRequest($urlString, $body, $contentType = null)
45
    {
46 54
        $url = parse_url($urlString);
47 24
        $data = '';
48 24
        if (array_key_exists('path', $url)) {
49 54
            $data = $url['path'];
50
        }
51
        if (array_key_exists('query', $url)) {
52
            $data .= '?' . $url['query'];
53
        }
54
        $data .= "\n";
55
56
        if ($body !== null && $contentType === 'application/x-www-form-urlencoded') {
57
            $data .= $body;
58 12
        }
59
        return $this->sign($data);
60 12
    }
61
62 12
    /**
63 12
     * @param string $urlString
64 9
     * @param string $method
65 9
     * @param string $body
66 3
     * @param null|Header $headers
67
     */
68 12
    public function signQiniuAuthorization($urlString, $method = "GET", $body = "", $headers = null)
69
    {
70 12
        $url = parse_url($urlString);
71 12
        if (!$url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
72
            return array(null, new \Exception("parse_url error"));
73
        }
74 21
75
        // append method, path and query
76 21
        if ($method === "") {
77 21
            $data = "GET ";
78 21
        } else {
79 15
            $data = $method . " ";
80 15
        }
81
        if (isset($url["path"])) {
82 21
            $data .= $url["path"];
83 21
        }
84 21
        if (isset($url["query"])) {
85
            $data .= "?" . $url["query"];
86 21
        }
87 21
88
        // append Host
89
        $data .= "\n";
90
        $data .= "Host: ";
91
        if (isset($url["host"])) {
92
            $data .= $url["host"];
93
        }
94
        if (isset($url["port"]) && $url["port"] > 0) {
95
            $data .= ":" . $url["port"];
96
        }
97
98
        // try to append content type
99
        if ($headers != null && isset($headers["Content-Type"])) {
100
            // append content type
101
            $data .= "\n";
102
            $data .= "Content-Type: " . $headers["Content-Type"];
103
        }
104
105
        // try append xQiniuHeaders
106
        if ($headers != null) {
107
            $headerLines = array();
108
            $keyPrefix = "X-Qiniu-";
109
            foreach ($headers as $k => $v) {
110
                if (strlen($k) > strlen($keyPrefix) && strpos($k, $keyPrefix) === 0) {
111
                    array_push(
112
                        $headerLines,
113
                        $k . ": " . $v
114
                    );
115
                }
116
            }
117
            if (count($headerLines) > 0) {
118
                $data .= "\n";
119
                sort($headerLines);
120
                $data .= implode("\n", $headerLines);
121
            }
122 21
        }
123
124 21
        // append body
125 18
        $data .= "\n\n";
126
        if (!is_null($body)
0 ignored issues
show
introduced by
The condition is_null($body) is always false.
Loading history...
127 3
            && strlen($body) > 0
128 3
            && isset($headers["Content-Type"])
129 3
            && $headers["Content-Type"] != "application/octet-stream"
130 3
        ) {
131 3
            $data .= $body;
132 3
        }
133
134
        return array($this->sign($data), null);
135 51
    }
136
137 51
    public function verifyCallback(
138 51
        $contentType,
139
        $originAuthorization,
140
        $url,
141
        $body,
142
        $method = "GET",
143
        $headers = array()
144
    ) {
145
        if (strpos($originAuthorization, 'Qiniu') === 0) {
146
            $qnHeaders = new Header($headers);
147
            if (!isset($qnHeaders['Content-Type'])) {
148
                $qnHeaders['Content-Type'] = $contentType;
149
            }
150
            list($sign, $err) = $this->signQiniuAuthorization(
151
                $url,
152
                $method,
153
                $body,
154
                $qnHeaders
155
            );
156
            if ($err !== null) {
157
                return false;
158
            }
159
            $authorization = 'Qiniu ' . $sign;
160
        } else {
161
            $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
162
        }
163
        return $originAuthorization === $authorization;
164
    }
165
166
    public function privateDownloadUrl($baseUrl, $expires = 3600)
167
    {
168
        $deadline = time() + $expires;
169
170
        $pos = strpos($baseUrl, '?');
171
        if ($pos !== false) {
172
            $baseUrl .= '&e=';
173
        } else {
174
            $baseUrl .= '?e=';
175
        }
176
        $baseUrl .= $deadline;
177
178
        $token = $this->sign($baseUrl);
179
        return "$baseUrl&token=$token";
180
    }
181
182
    public function uploadToken($bucket, $key = null, $expires = 3600, $policy = null, $strictPolicy = true)
183
    {
184
        $deadline = time() + $expires;
185
        $scope = $bucket;
186
        if ($key !== null) {
187
            $scope .= ':' . $key;
188
        }
189
190
        $args = self::copyPolicy($args, $policy, $strictPolicy);
191
        $args['scope'] = $scope;
192
        $args['deadline'] = $deadline;
193
194
        $b = json_encode($args);
195
        return $this->signWithData($b);
196
    }
197
198
    /**
199
     *上传策略,参数规格详见
200
     *http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
201
     */
202
    private static $policyFields = array(
203
        'callbackUrl',
204
        'callbackBody',
205
        'callbackHost',
206
        'callbackBodyType',
207
        'callbackFetchKey',
208
209
        'returnUrl',
210
        'returnBody',
211
212
        'endUser',
213
        'saveKey',
214
        'forceSaveKey',
215
        'insertOnly',
216
217
        'detectMime',
218
        'mimeLimit',
219
        'fsizeMin',
220
        'fsizeLimit',
221
222
        'persistentOps', // 与 persistentWorkflowTemplateID 二选一
223
        'persistentNotifyUrl',
224
        'persistentPipeline',
225
        'persistentType', // 为 `1` 时开启闲时任务
226
        'persistentWorkflowTemplateID', // 与 persistentOps 二选一
227
228
        'deleteAfterDays',
229
        'fileType',
230
        'isPrefixalScope',
231
232
        'transform', // deprecated
233
        'transformFallbackKey', // deprecated
234
        'transformFallbackMode', // deprecated
235
    );
236
237
    private static function copyPolicy(&$policy, $originPolicy, $strictPolicy)
238
    {
239
        if ($originPolicy === null) {
240
            return array();
241
        }
242
        foreach ($originPolicy as $key => $value) {
243
            if (!$strictPolicy || in_array((string)$key, self::$policyFields, true)) {
244
                $policy[$key] = $value;
245
            }
246
        }
247
        return $policy;
248
    }
249
250
    public function authorization($url, $body = null, $contentType = null)
251
    {
252
        $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
253
        return array('Authorization' => $authorization);
254
    }
255
256
    public function authorizationV2($url, $method, $body = null, $contentType = null)
257
    {
258
        $headers = new Header();
259
        $result = array();
260
        if ($contentType != null) {
261
            $headers['Content-Type'] = $contentType;
262
            $result['Content-Type'] = $contentType;
263
        }
264
265
        $signDate = gmdate('Ymd\THis\Z', time());
266
        if ($this->options['disableQiniuTimestampSignature'] !== null) {
267
            if (!$this->options['disableQiniuTimestampSignature']) {
268
                $headers['X-Qiniu-Date'] = $signDate;
269
                $result['X-Qiniu-Date'] = $signDate;
270
            }
271
        } elseif (getenv("DISABLE_QINIU_TIMESTAMP_SIGNATURE")) {
272
            if (strtolower(getenv("DISABLE_QINIU_TIMESTAMP_SIGNATURE")) !== "true") {
273
                $headers['X-Qiniu-Date'] = $signDate;
274
                $result['X-Qiniu-Date'] = $signDate;
275
            }
276
        } else {
277
            $headers['X-Qiniu-Date'] = $signDate;
278
            $result['X-Qiniu-Date'] = $signDate;
279
        }
280
281
        list($sign) = $this->signQiniuAuthorization($url, $method, $body, $headers);
282
        $result['Authorization'] = 'Qiniu ' . $sign;
283
        return $result;
284
    }
285
}
286