Completed
Push — develop ( c28702...43dcba )
by Fabian
01:22
created

Decoder::decode()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 52
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 26
c 1
b 0
f 0
nc 7
nop 1
dl 0
loc 52
rs 8.8817

How to fix   Long Method   

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
declare(strict_types=1);
4
5
namespace Cakasim\Payone\Sdk\Redirect\Token\Format;
6
7
use Cakasim\Payone\Sdk\Config\ConfigExceptionInterface;
8
use Cakasim\Payone\Sdk\Config\ConfigInterface;
9
use Cakasim\Payone\Sdk\Redirect\Token\Token;
10
use Cakasim\Payone\Sdk\Redirect\Token\TokenInterface;
11
12
/**
13
 * @author Fabian Böttcher <[email protected]>
14
 * @since 0.1.0
15
 */
16
class Decoder implements DecoderInterface
17
{
18
    /**
19
     * @var ConfigInterface The SDK config.
20
     */
21
    protected $config;
22
23
    /**
24
     * @var SignerInterface The token signer.
25
     */
26
    protected $signer;
27
28
    /**
29
     * Constructs the token encoder.
30
     *
31
     * @param ConfigInterface $config The SDK config.
32
     * @param SignerInterface $signer The token signer.
33
     */
34
    public function __construct(ConfigInterface $config, SignerInterface $signer)
35
    {
36
        $this->config = $config;
37
        $this->signer = $signer;
38
    }
39
40
    /**
41
     * @inheritDoc
42
     */
43
    public function decode(string $token): TokenInterface
44
    {
45
        $token = explode('.', $token, 4);
46
47
        if (count($token) !== 3) {
48
            throw new DecoderException("Failed token decoding, the token encoded format is invalid.");
49
        }
50
51
        // Collect parts of the token.
52
        $iv = $token[0];
53
        $signature = $token[2];
54
        $token = $token[1];
55
56
        // Decode the URL safe encoded token parts.
57
        $iv = $this->urlSafeDecode($iv);
58
        $signature = $this->urlSafeDecode($signature);
59
        $token = $this->urlSafeDecode($token);
60
61
        try {
62
            // Create the expected (valid) signature of the token payload data.
63
            $expectedSignature = $this->signer->createSignature($token);
64
        } catch (SignerExceptionInterface $e) {
65
            throw new DecoderException("Failed token decoding, could not create trusted token signature.", 0, $e);
66
        }
67
68
        // Ensure that signatures match.
69
        if ($signature !== $expectedSignature) {
70
            throw new DecoderException("Failed token decoding, the token signature is invalid.");
71
        }
72
73
        try {
74
            // Load token encryption config.
75
            $encryptionMethod = $this->config->get('redirect.token_encryption_method');
76
            $encryptionKey = $this->config->get('redirect.token_encryption_key');
77
        } catch (ConfigExceptionInterface $e) {
78
            throw new DecoderException("Failed token decoding, the token encryption config is incomplete.", 0, $e);
79
        }
80
81
        // Get binary SHA-256 hash of cleartext encryption key.
82
        $encryptionKey = hash('sha256', $encryptionKey, true);
83
84
        // Decrypt the token payload data.
85
        $token = $this->decrypt($token, $encryptionMethod, $encryptionKey, $iv);
86
87
        // JSON decode the token payload.
88
        $token = json_decode($token, true);
89
90
        if (!is_array($token)) {
91
            throw new DecoderException("Failed token decoding, the token payload data is invalid.");
92
        }
93
94
        return new Token($token);
95
    }
96
97
    /**
98
     * Decodes the provided URL-safe encoded data.
99
     *
100
     * @param string $data The encoded data to decode.
101
     * @return string The decoded data.
102
     * @throws DecoderExceptionInterface If decoding fails.
103
     */
104
    protected function urlSafeDecode(string $data): string
105
    {
106
        $data = strtr($data, '-_', '+/');
107
        $data = base64_decode($data, true);
108
109
        if ($data === false) {
110
            throw new DecoderException("Failed token decoding, the token could not be base64 decoding.");
111
        }
112
113
        return $data;
114
    }
115
116
    /**
117
     * Decrypts the provided data.
118
     *
119
     * @param string $data The data to decrypt.
120
     * @param string $method The cipher method.
121
     * @param string $key The encryption key.
122
     * @param string $iv The initialization vector.
123
     * @return string The decrypted data.
124
     * @throws DecoderException If the decryption fails.
125
     */
126
    protected function decrypt(string $data, string $method, string $key, string $iv): string
127
    {
128
        $data = openssl_decrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
129
130
        if (!is_string($data)) {
0 ignored issues
show
introduced by
The condition is_string($data) is always true.
Loading history...
131
            throw new DecoderException("Failed token decoding, the token could not be decrypted.");
132
        }
133
134
        return $data;
135
    }
136
}
137