Completed
Push — master ( d1be6d...8440b6 )
by Andrey
07:58
created

generateServiceSharedAccessSignatureToken()   C

Complexity

Conditions 12
Paths 162

Size

Total Lines 140

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 162
nop 18
dl 0
loc 140
rs 5.16
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * LICENSE: The MIT License (the "License")
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 * https://github.com/azure/azure-storage-php/LICENSE
8
 *
9
 * Unless required by applicable law or agreed to in writing, software
10
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 * See the License for the specific language governing permissions and
13
 * limitations under the License.
14
 *
15
 * PHP version 5
16
 *
17
 * @category  Microsoft
18
 * @package   MicrosoftAzure\Storage\Common\Internal\Authentication
19
 * @author    Azure Storage PHP SDK <[email protected]>
20
 * @copyright Microsoft Corporation
21
 * @license   https://github.com/azure/azure-storage-php/LICENSE
22
 * @link      https://github.com/azure/azure-storage-php
23
 */
24
25
namespace MicrosoftAzure\Storage\Common;
26
27
use MicrosoftAzure\Storage\Common\Internal\Resources;
28
use MicrosoftAzure\Storage\Common\Internal\Utilities;
29
use MicrosoftAzure\Storage\Common\Internal\Validate;
30
use MicrosoftAzure\Storage\Common\Models\AccessPolicy;
31
32
/**
33
 * Provides methods to generate Azure Storage Shared Access Signature
34
 *
35
 * @category  Microsoft
36
 * @package   MicrosoftAzure\Storage\Common\Internal\Authentication
37
 * @author    Azure Storage PHP SDK <[email protected]>
38
 * @copyright 2017 Microsoft Corporation
39
 * @license   https://github.com/azure/azure-storage-php/LICENSE
40
 * @link      https://github.com/azure/azure-storage-php
41
 */
42
class SharedAccessSignatureHelper
43
{
44
    protected $accountName;
45
    protected $accountKey;
46
47
    /**
48
     * Constructor.
49
     *
50
     * @param string $accountName the name of the storage account.
51
     * @param string $accountKey the shared key of the storage account
52
     *
53
     * @return
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
54
     * MicrosoftAzure\Storage\Common\SharedAccessSignatureHelper
55
     */
56
    public function __construct($accountName, $accountKey)
57
    {
58
        Validate::isString($accountName, 'accountName');
59
        Validate::notNullOrEmpty($accountName, 'accountName');
60
61
        Validate::isString($accountKey, 'accountKey');
62
        Validate::notNullOrEmpty($accountKey, 'accountKey');
63
64
        $this->accountName = urldecode($accountName);
65
        $this->accountKey = $accountKey;
66
    }
67
68
    /**
69
     * Helper function to generate a service shared access signature.
70
     *
71
     * This only supports version 2015-04-05 and later.
72
     *
73
     * @param  string $signedService        The service type of the SAS.
74
     * @param  string $signedResource       Resource name to generate the
75
     *                                      canonicalized resource.
76
     * @param  string $resourceName         The name of the resource.
77
     * @param  string $signedPermissions    Signed permissions.
78
     * @param  string $signedExpiry         Signed expiry date.
79
     * @param  string $signedStart          Signed start date.
80
     * @param  string $signedIP             Signed IP address.
81
     * @param  string $signedProtocol       Signed protocol.
82
     * @param  string $signedIdentifier     Signed identifier.
83
     * @param  string $cacheControl         Cache-Control header (rscc).
84
     * @param  string $contentDisposition   Content-Disposition header (rscd).
85
     * @param  string $contentEncoding      Content-Encoding header (rsce).
86
     * @param  string $contentLanguage      Content-Language header (rscl).
87
     * @param  string $contentType          Content-Type header (rsct).
88
     * @param  string $startingPartitionKey Minimum partition key.
89
     * @param  string $startingRowKey       Minimum row key.
90
     * @param  string $endingPartitionKey   Maximum partition key.
91
     * @param  string $endingRowKey         Maximum row key.
92
     *
93
     * @see Constructing an service SAS at
94
     * https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
95
     * @return string
96
     */
97
    private function generateServiceSharedAccessSignatureToken(
98
        $signedService,
99
        $signedResource,
100
        $resourceName,
101
        $signedPermissions,
102
        $signedExpiry,
103
        $signedStart = "",
104
        $signedIP = "",
105
        $signedProtocol = "",
106
        $signedIdentifier = "",
107
        $cacheControl = "",
108
        $contentDisposition = "",
109
        $contentEncoding = "",
110
        $contentLanguage = "",
111
        $contentType = "",
112
        $startingPartitionKey = "",
113
        $startingRowKey = "",
114
        $endingPartitionKey = "",
115
        $endingRowKey = ""
116
    ) {
117
        //Since every version of client library should only be able to generate
118
        //the same service version as its targets, the signed version is fixed here.
119
        $signedVersion = Resources::STORAGE_API_LATEST_VERSION;
120
        // check that the resource name is valid.
121
        Validate::notNullOrEmpty($resourceName, 'resourceName');
122
        Validate::isString($resourceName, 'resourceName');
123
        // validate and sanitize signed permissions
124
        $signedPermissions = $this->validateAndSanitizeSignedPermissions(
125
            $signedPermissions,
126
            $signedResource
127
        );
128
        // check that expiracy is valid
129
        Validate::notNullOrEmpty($signedExpiry, 'signedExpiry');
130
        Validate::isString($signedExpiry, 'signedExpiry');
131
        Validate::isDateString($signedExpiry, 'signedExpiry');
132
        // check that signed start is valid
133
        Validate::isString($signedStart, 'signedStart');
134
        if (strlen($signedStart) > 0) {
135
            Validate::isDateString($signedStart, 'signedStart');
136
        }
137
        // check that signed IP is valid
138
        Validate::isString($signedIP, 'signedIP');
139
        // validate and sanitize signed protocol
140
        $signedProtocol = $this->validateAndSanitizeSignedProtocol($signedProtocol);
141
        // check that signed identifier is valid
142
        Validate::isString($signedIdentifier, 'signedIdentifier');
143
        Validate::isTrue(
144
            strlen($signedIdentifier) <= 64,
145
            sprintf(Resources::INVALID_STRING_LENGTH, 'signedIdentifier', 'maximum 64')
146
        );
147
        //Categorize the type of the resource for future usage.
148
        $type = '';
149
        if ($signedService == Resources::RESOURCE_TYPE_BLOB ||
150
            $signedService == Resources::RESOURCE_TYPE_FILE
151
        ) {
152
            $type = 'bf';
153
        } elseif ($signedService == Resources::RESOURCE_TYPE_TABLE) {
154
            $type = 't';
155
        }
156
157
        if ($type === 'bf') {
158
            Validate::isString($cacheControl, 'cacheControl');
159
            Validate::isString($contentDisposition, 'contentDisposition');
160
            Validate::isString($contentEncoding, 'contentEncoding');
161
            Validate::isString($contentLanguage, 'contentLanguage');
162
            Validate::isString($contentType, 'contentType');
163
        } elseif ($type === 't') {
164
            Validate::isString($startingPartitionKey, 'startingPartitionKey');
165
            Validate::isString($startingRowKey, 'startingRowKey');
166
            Validate::isString($endingPartitionKey, 'endingPartitionKey');
167
            Validate::isString($endingRowKey, 'endingRowKey');
168
        }
169
170
        // construct an array with the parameters to generate the shared access signature at the account level
171
        $parameters = array();
172
        $parameters[] = urldecode($signedPermissions);
173
        $parameters[] = urldecode($signedStart);
174
        $parameters[] = urldecode($signedExpiry);
175
        $parameters[] = urldecode(static::generateCanonicalResource(
0 ignored issues
show
Bug introduced by
Since generateCanonicalResource() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of generateCanonicalResource() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
176
            $this->accountName,
177
            $signedService,
178
            $resourceName
179
        ));
180
        $parameters[] = urldecode($signedIdentifier);
181
        $parameters[] = urldecode($signedIP);
182
        $parameters[] = urldecode($signedProtocol);
183
        $parameters[] = urldecode($signedVersion);
184
        if ($type === 'bf') {
185
            $parameters[] = urldecode($cacheControl);
186
            $parameters[] = urldecode($contentDisposition);
187
            $parameters[] = urldecode($contentEncoding);
188
            $parameters[] = urldecode($contentLanguage);
189
            $parameters[] = urldecode($contentType);
190
        } elseif ($type === 't') {
191
            $parameters[] = urldecode($startingPartitionKey);
192
            $parameters[] = urldecode($startingRowKey);
193
            $parameters[] = urldecode($endingPartitionKey);
194
            $parameters[] = urldecode($endingRowKey);
195
        }
196
        
197
        // implode the parameters into a string
198
        $stringToSign = utf8_encode(implode("\n", $parameters));
199
        // decode the account key from base64
200
        $decodedAccountKey = base64_decode($this->accountKey);
201
        // create the signature with hmac sha256
202
        $signature = hash_hmac("sha256", $stringToSign, $decodedAccountKey, true);
203
        // encode the signature as base64
204
        $sig = urlencode(base64_encode($signature));
205
206
        $buildOptQueryStr = function ($string, $abrv) {
207
            return $string === '' ? '' : $abrv . $string;
208
        };
209
        //adding all the components for account SAS together.
210
        $sas  = 'sv='    . $signedVersion;
211
        if ($type === 'bf') {
212
            $sas .= '&sr='   . $signedResource;
213
            $sas .= $buildOptQueryStr($cacheControl, '&rscc=');
214
            $sas .= $buildOptQueryStr($contentDisposition, '&rscd=');
215
            $sas .= $buildOptQueryStr($contentEncoding, '&rsce=');
216
            $sas .= $buildOptQueryStr($contentLanguage, '&rscl=');
217
            $sas .= $buildOptQueryStr($contentType, '&rsct=');
218
        } elseif ($type === 't') {
219
            $sas .= '&tn='   . $resourceName;
220
            $sas .= $buildOptQueryStr($startingPartitionKey, '&spk=');
221
            $sas .= $buildOptQueryStr($startingRowKey, '&srk=');
222
            $sas .= $buildOptQueryStr($endingPartitionKey, '&epk=');
223
            $sas .= $buildOptQueryStr($endingRowKey, '&erk=');
224
        }
225
226
        $sas .= $buildOptQueryStr($signedStart, '&st=');
227
        $sas .= '&se='   . $signedExpiry;
228
        $sas .= '&sp='   . $signedPermissions;
229
        $sas .= $buildOptQueryStr($signedIP, '&sip=');
230
        $sas .= $buildOptQueryStr($signedProtocol, '&spr=');
231
        $sas .= $buildOptQueryStr($signedIdentifier, '&si=');
232
        $sas .= '&sig='  . $sig;
233
234
        // return the signature
235
        return $sas;
236
    }
237
238
    /**
239
     * Generates Blob service shared access signature.
240
     *
241
     * This only supports version 2015-04-05 and later.
242
     *
243
     * @param  string $signedResource     Resource name to generate the
244
     *                                    canonicalized resource.
245
     *                                    It can be Resources::RESOURCE_TYPE_BLOB
246
     *                                    or Resources::RESOURCE_TYPE_CONTAINER.
247
     * @param  string $resourceName       The name of the resource, including
248
     *                                    the path of the resource. It should be
249
     *                                    - {container}/{blob}: for blobs,
250
     *                                    - {container}: for containers, e.g.:
251
     *                                    /mymusic/music.mp3 or
252
     *                                    music.mp3
253
     * @param  string $signedPermissions  Signed permissions.
254
     * @param  string $signedExpiry       Signed expiry date.
255
     * @param  string $signedStart        Signed start date.
256
     * @param  string $signedIP           Signed IP address.
257
     * @param  string $signedProtocol     Signed protocol.
258
     * @param  string $signedIdentifier   Signed identifier.
259
     * @param  string $cacheControl       Cache-Control header (rscc).
260
     * @param  string $contentDisposition Content-Disposition header (rscd).
261
     * @param  string $contentEncoding    Content-Encoding header (rsce).
262
     * @param  string $contentLanguage    Content-Language header (rscl).
263
     * @param  string $contentType        Content-Type header (rsct).
264
     *
265
     * @see Constructing an service SAS at
266
     * https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
267
     * @return string
268
     */
269 View Code Duplication
    public function generateBlobServiceSharedAccessSignatureToken(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
        $signedResource,
271
        $resourceName,
272
        $signedPermissions,
273
        $signedExpiry,
274
        $signedStart = "",
275
        $signedIP = "",
276
        $signedProtocol = "",
277
        $signedIdentifier = "",
278
        $cacheControl = "",
279
        $contentDisposition = "",
280
        $contentEncoding = "",
281
        $contentLanguage = "",
282
        $contentType = ""
283
    ) {
284
        // check that the resource name is valid.
285
        Validate::isString($signedResource, 'signedResource');
286
        Validate::notNullOrEmpty($signedResource, 'signedResource');
287
        Validate::isTrue(
288
            $signedResource == Resources::RESOURCE_TYPE_BLOB ||
289
            $signedResource == Resources::RESOURCE_TYPE_CONTAINER,
290
            \sprintf(
291
                Resources::INVALID_VALUE_MSG,
292
                '$signedResource',
293
                'Can only be \'b\' or \'c\'.'
294
            )
295
        );
296
297
        return $this->generateServiceSharedAccessSignatureToken(
298
            Resources::RESOURCE_TYPE_BLOB,
299
            $signedResource,
300
            $resourceName,
301
            $signedPermissions,
302
            $signedExpiry,
303
            $signedStart,
304
            $signedIP,
305
            $signedProtocol,
306
            $signedIdentifier,
307
            $cacheControl,
308
            $contentDisposition,
309
            $contentEncoding,
310
            $contentLanguage,
311
            $contentType,
312
            '',
313
            '',
314
            '',
315
            ''
316
        );
317
    }
318
319
    /**
320
     * Generates File service shared access signature.
321
     *
322
     * This only supports version 2015-04-05 and later.
323
     *
324
     * @param  string $signedResource     Resource name to generate the
325
     *                                    canonicalized resource.
326
     *                                    It can be Resources::RESOURCE_TYPE_FILE
327
     *                                    or Resources::RESOURCE_TYPE_SHARE.
328
     * @param  string $resourceName       The name of the resource, including
329
     *                                    the path of the resource. It should be
330
     *                                    - {share}/{file}: for files,
331
     *                                    - {share}: for shares, e.g.:
332
     *                                    /mymusic/music.mp3 or
333
     *                                    music.mp3
334
     * @param  string $signedPermissions  Signed permissions.
335
     * @param  string $signedExpiry       Signed expiry date.
336
     * @param  string $signedStart        Signed start date.
337
     * @param  string $signedIP           Signed IP address.
338
     * @param  string $signedProtocol     Signed protocol.
339
     * @param  string $signedIdentifier   Signed identifier.
340
     * @param  string $cacheControl       Cache-Control header (rscc).
341
     * @param  string $contentDisposition Content-Disposition header (rscd).
342
     * @param  string $contentEncoding    Content-Encoding header (rsce).
343
     * @param  string $contentLanguage    Content-Language header (rscl).
344
     * @param  string $contentType        Content-Type header (rsct).
345
     *
346
     * @see Constructing an service SAS at
347
     * https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
348
     * @return string
349
     */
350 View Code Duplication
    public function generateFileServiceSharedAccessSignatureToken(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
351
        $signedResource,
352
        $resourceName,
353
        $signedPermissions,
354
        $signedExpiry,
355
        $signedStart = "",
356
        $signedIP = "",
357
        $signedProtocol = "",
358
        $signedIdentifier = "",
359
        $cacheControl = "",
360
        $contentDisposition = "",
361
        $contentEncoding = "",
362
        $contentLanguage = "",
363
        $contentType = ""
364
    ) {
365
        // check that the resource name is valid.
366
        Validate::isString($signedResource, 'signedResource');
367
        Validate::notNullOrEmpty($signedResource, 'signedResource');
368
        Validate::isTrue(
369
            $signedResource == Resources::RESOURCE_TYPE_FILE ||
370
            $signedResource == Resources::RESOURCE_TYPE_SHARE,
371
            \sprintf(
372
                Resources::INVALID_VALUE_MSG,
373
                '$signedResource',
374
                'Can only be \'f\' or \'s\'.'
375
            )
376
        );
377
378
        return $this->generateServiceSharedAccessSignatureToken(
379
            Resources::RESOURCE_TYPE_FILE,
380
            $signedResource,
381
            $resourceName,
382
            $signedPermissions,
383
            $signedExpiry,
384
            $signedStart,
385
            $signedIP,
386
            $signedProtocol,
387
            $signedIdentifier,
388
            $cacheControl,
389
            $contentDisposition,
390
            $contentEncoding,
391
            $contentLanguage,
392
            $contentType,
393
            '',
394
            '',
395
            '',
396
            ''
397
        );
398
    }
399
400
    /**
401
     * Generates Table service shared access signature.
402
     *
403
     * This only supports version 2015-04-05 and later.
404
     *
405
     * @param  string $tableName            The name of the table.
406
     * @param  string $signedPermissions    Signed permissions.
407
     * @param  string $signedExpiry         Signed expiry date.
408
     * @param  string $signedStart          Signed start date.
409
     * @param  string $signedIP             Signed IP address.
410
     * @param  string $signedProtocol       Signed protocol.
411
     * @param  string $signedIdentifier     Signed identifier.
412
     * @param  string $startingPartitionKey Minimum partition key.
413
     * @param  string $startingRowKey       Minimum row key.
414
     * @param  string $endingPartitionKey   Maximum partition key.
415
     * @param  string $endingRowKey         Maximum row key.
416
     *
417
     * @see Constructing an service SAS at
418
     * https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
419
     * @return string
420
     */
421
    public function generateTableServiceSharedAccessSignatureToken(
422
        $tableName,
423
        $signedPermissions,
424
        $signedExpiry,
425
        $signedStart = "",
426
        $signedIP = "",
427
        $signedProtocol = "",
428
        $signedIdentifier = "",
429
        $startingPartitionKey = "",
430
        $startingRowKey = "",
431
        $endingPartitionKey = "",
432
        $endingRowKey = ""
433
    ) {
434
        return $this->generateServiceSharedAccessSignatureToken(
435
            Resources::RESOURCE_TYPE_TABLE,
436
            Resources::RESOURCE_TYPE_TABLE,
437
            $tableName,
438
            $signedPermissions,
439
            $signedExpiry,
440
            $signedStart,
441
            $signedIP,
442
            $signedProtocol,
443
            $signedIdentifier,
444
            '',
445
            '',
446
            '',
447
            '',
448
            '',
449
            $startingPartitionKey,
450
            $startingRowKey,
451
            $endingPartitionKey,
452
            $endingRowKey
453
        );
454
    }
455
456
    /**
457
     * Generates a queue service shared access signature.
458
     *
459
     * This only supports version 2015-04-05 and later.
460
     *
461
     * @param  string $queueName          The name of the queue.
462
     * @param  string $signedPermissions  Signed permissions.
463
     * @param  string $signedExpiry       Signed expiry date.
464
     * @param  string $signedStart        Signed start date.
465
     * @param  string $signedIdentifier   Signed identifier.
466
     * @param  string $signedIP           Signed IP address.
467
     * @param  string $signedProtocol     Signed protocol.
468
     *
469
     * @see Constructing an service SAS at
470
     * https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
471
     * @return string
472
     */
473
    public function generateQueueServiceSharedAccessSignatureToken(
474
        $queueName,
475
        $signedPermissions,
476
        $signedExpiry,
477
        $signedStart = "",
478
        $signedIP = "",
479
        $signedProtocol = "",
480
        $signedIdentifier = ""
481
    ) {
482
        return $this->generateServiceSharedAccessSignatureToken(
483
            Resources::RESOURCE_TYPE_QUEUE,
484
            Resources::RESOURCE_TYPE_QUEUE,
485
            $queueName,
486
            $signedPermissions,
487
            $signedExpiry,
488
            $signedStart,
489
            $signedIP,
490
            $signedProtocol,
491
            $signedIdentifier,
492
            '',
493
            '',
494
            '',
495
            '',
496
            '',
497
            '',
498
            '',
499
            '',
500
            ''
501
        );
502
    }
503
504
    /**
505
     * Generates a shared access signature at the account level.
506
     *
507
     * @param string $signedVersion         Specifies the signed version to use.
508
     * @param string $signedPermissions     Specifies the signed permissions for
509
     *                                      the account SAS.
510
     * @param string $signedService         Specifies the signed services
511
     *                                      accessible with the account SAS.
512
     * @param string $signedResourceType    Specifies the signed resource types
513
     *                                      that are accessible with the account
514
     *                                      SAS.
515
     * @param string $signedExpiry          The time at which the shared access
516
     *                                      signature becomes invalid, in an ISO
517
     *                                      8601 format.
518
     * @param string $signedStart           The time at which the SAS becomes
519
     *                                      valid, in an ISO 8601 format.
520
     * @param string $signedIP              Specifies an IP address or a range
521
     *                                      of IP addresses from which to accept
522
     *                                      requests.
523
     * @param string $signedProtocol        Specifies the protocol permitted for
524
     *                                      a request made with the account SAS.
525
     *
526
     * @see Constructing an account SAS at
527
     *      https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/constructing-an-account-sas
528
     *
529
     * @return string
530
     */
531
    public function generateAccountSharedAccessSignatureToken(
532
        $signedVersion,
533
        $signedPermissions,
534
        $signedService,
535
        $signedResourceType,
536
        $signedExpiry,
537
        $signedStart = "",
538
        $signedIP = "",
539
        $signedProtocol = ""
540
    ) {
541
        // check that version is valid
542
        Validate::isString($signedVersion, 'signedVersion');
543
        Validate::notNullOrEmpty($signedVersion, 'signedVersion');
544
        Validate::isDateString($signedVersion, 'signedVersion');
545
546
        // validate and sanitize signed service
547
        $signedService = $this->validateAndSanitizeSignedService($signedService);
548
549
        // validate and sanitize signed resource type
550
        $signedResourceType = $this->validateAndSanitizeSignedResourceType($signedResourceType);
551
        
552
        // validate and sanitize signed permissions
553
        $signedPermissions = $this->validateAndSanitizeSignedPermissions($signedPermissions);
554
555
        // check that expiracy is valid
556
        Validate::isString($signedExpiry, 'signedExpiry');
557
        Validate::notNullOrEmpty($signedExpiry, 'signedExpiry');
558
        Validate::isDateString($signedExpiry, 'signedExpiry');
559
560
        // check that signed start is valid
561
        Validate::isString($signedStart, 'signedStart');
562
        if (strlen($signedStart) > 0) {
563
            Validate::isDateString($signedStart, 'signedStart');
564
        }
565
566
        // check that signed IP is valid
567
        Validate::isString($signedIP, 'signedIP');
568
569
        // validate and sanitize signed protocol
570
        $signedProtocol = $this->validateAndSanitizeSignedProtocol($signedProtocol);
571
572
        // construct an array with the parameters to generate the shared access signature at the account level
573
        $parameters = array();
574
        $parameters[] = $this->accountName;
575
        $parameters[] = urldecode($signedPermissions);
576
        $parameters[] = urldecode($signedService);
577
        $parameters[] = urldecode($signedResourceType);
578
        $parameters[] = urldecode($signedStart);
579
        $parameters[] = urldecode($signedExpiry);
580
        $parameters[] = urldecode($signedIP);
581
        $parameters[] = urldecode($signedProtocol);
582
        $parameters[] = urldecode($signedVersion);
583
584
        // implode the parameters into a string
585
        $stringToSign = utf8_encode(implode("\n", $parameters) . "\n");
586
587
        // decode the account key from base64
588
        $decodedAccountKey = base64_decode($this->accountKey);
589
        
590
        // create the signature with hmac sha256
591
        $signature = hash_hmac("sha256", $stringToSign, $decodedAccountKey, true);
592
593
        // encode the signature as base64 and url encode.
594
        $sig = urlencode(base64_encode($signature));
595
596
        //adding all the components for account SAS together.
597
        $sas  = 'sv=' . $signedVersion;
598
        $sas .= '&ss=' . $signedService;
599
        $sas .= '&srt=' . $signedResourceType;
600
        $sas .= '&sp=' . $signedPermissions;
601
        $sas .= '&se=' . $signedExpiry;
602
        $sas .= $signedStart === ''? '' : '&st=' . $signedStart;
603
        $sas .= $signedIP === ''? '' : '&sip=' . $signedIP;
604
        $sas .= '&spr=' . $signedProtocol;
605
        $sas .= '&sig=' . $sig;
606
607
        // return the signature
608
        return $sas;
609
    }
610
611
    /**
612
     * Validates and sanitizes the signed service parameter
613
     *
614
     * @param string $signedService Specifies the signed services accessible
615
     *                              with the account SAS.
616
     *
617
     * @return string
618
     */
619 View Code Duplication
    private function validateAndSanitizeSignedService($signedService)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
620
    {
621
        // validate signed service is not null or empty
622
        Validate::isString($signedService, 'signedService');
623
        Validate::notNullOrEmpty($signedService, 'signedService');
624
625
        // The signed service should only be a combination of the letters b(lob) q(ueue) t(able) or f(ile)
626
        $validServices = ['b', 'q', 't', 'f'];
627
628
        return $this->validateAndSanitizeStringWithArray(
629
            strtolower($signedService),
630
            $validServices
631
        );
632
    }
633
634
    /**
635
     * Validates and sanitizes the signed resource type parameter
636
     *
637
     * @param string $signedResourceType    Specifies the signed resource types
638
     *                                      that are accessible with the account
639
     *                                      SAS.
640
     *
641
     * @return string
642
     */
643 View Code Duplication
    private function validateAndSanitizeSignedResourceType($signedResourceType)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
644
    {
645
        // validate signed resource type is not null or empty
646
        Validate::isString($signedResourceType, 'signedResourceType');
647
        Validate::notNullOrEmpty($signedResourceType, 'signedResourceType');
648
649
        // The signed resource type should only be a combination of the letters s(ervice) c(container) or o(bject)
650
        $validResourceTypes = ['s', 'c', 'o'];
651
652
        return $this->validateAndSanitizeStringWithArray(
653
            strtolower($signedResourceType),
654
            $validResourceTypes
655
        );
656
    }
657
658
    /**
659
     * Validates and sanitizes the signed permissions parameter
660
     *
661
     * @param string $signedPermissions Specifies the signed permissions for the
662
     *                                  account SAS.
663
     * @param string $signedResource    Specifies the signed resource for the
664
     *
665
     * @return string
666
     */
667
    private function validateAndSanitizeSignedPermissions(
668
        $signedPermissions,
669
        $signedResource = ''
670
    ) {
671
        // validate signed permissions are not null or empty
672
        Validate::isString($signedPermissions, 'signedPermissions');
673
        Validate::notNullOrEmpty($signedPermissions, 'signedPermissions');
674
675
        if ($signedResource == '') {
676
            $validPermissions = ['r', 'w', 'd', 'l', 'a', 'c', 'u', 'p'];
677
        } else {
678
            $validPermissions =
679
                AccessPolicy::getResourceValidPermissions()[$signedResource];
680
        }
681
682
        return $this->validateAndSanitizeStringWithArray(
683
            strtolower($signedPermissions),
684
            $validPermissions
685
        );
686
    }
687
688
    /**
689
     * Validates and sanitizes the signed protocol parameter
690
     *
691
     * @param string $signedProtocol Specifies the signed protocol for the
692
     *                               account SAS.
693
694
     * @return string
695
     */
696
    private function validateAndSanitizeSignedProtocol($signedProtocol)
697
    {
698
        Validate::isString($signedProtocol, 'signedProtocol');
699
        // sanitize string
700
        $sanitizedSignedProtocol = strtolower($signedProtocol);
701
        if (strlen($sanitizedSignedProtocol) > 0) {
702
            if (strcmp($sanitizedSignedProtocol, "https") != 0 && strcmp($sanitizedSignedProtocol, "https,http") != 0) {
703
                throw new \InvalidArgumentException(Resources::SIGNED_PROTOCOL_INVALID_VALIDATION_MSG);
704
            }
705
        }
706
707
        return $sanitizedSignedProtocol;
708
    }
709
710
    /**
711
     * Checks if a string contains an other string
712
     *
713
     * @param string $input        The input to test.
714
     * @param string $toFind       The string to find in the input.
715
716
     * @return bool
717
     */
718
    private function strcontains($input, $toFind)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
719
    {
720
        return strpos($input, $toFind) !== false;
721
    }
722
723
    /**
724
     * Removes duplicate characters from a string
725
     *
726
     * @param string $input        The input string.
727
728
     * @return string
729
     */
730
    private function validateAndSanitizeStringWithArray($input, array $array)
731
    {
732
        $result = '';
733
        foreach ($array as $value) {
734
            if (strpos($input, $value) !== false) {
735
                //append the valid permission to result.
736
                $result .= $value;
737
                //remove all the character that represents the permission.
738
                $input = str_replace(
739
                    $value,
740
                    '',
741
                    $input
742
                );
743
            }
744
        }
745
746
        Validate::isTrue(
747
            strlen($input) == '',
748
            sprintf(
749
                Resources::STRING_NOT_WITH_GIVEN_COMBINATION,
750
                implode(', ', $array)
751
            )
752
        );
753
        return $result;
754
    }
755
756
757
    /**
758
     * Generate the canonical resource using the given account name, service
759
     * type and resource.
760
     *
761
     * @param  string $accountName The account name of the service.
762
     * @param  string $service     The service name of the service.
763
     * @param  string $resource    The name of the resource.
764
     *
765
     * @return string
766
     */
767
    private static function generateCanonicalResource(
768
        $accountName,
769
        $service,
770
        $resource
771
    ) {
772
        static $serviceMap = array(
773
            Resources::RESOURCE_TYPE_BLOB  => 'blob',
774
            Resources::RESOURCE_TYPE_FILE  => 'file',
775
            Resources::RESOURCE_TYPE_QUEUE => 'queue',
776
            Resources::RESOURCE_TYPE_TABLE => 'table',
777
        );
778
        $serviceName = $serviceMap[$service];
779
        if (Utilities::startsWith($resource, '/')) {
780
            $resource = substr(1, strlen($resource) - 1);
781
        }
782
        return sprintf('/%s/%s/%s', $serviceName, $accountName, $resource);
783
    }
784
}
785