Passed
Push — master ( fd1e7e...0a2e77 )
by Alex
02:12
created

WebPush::sendMessage()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 48
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 6.0014

Importance

Changes 0
Metric Value
eloc 29
dl 0
loc 48
ccs 28
cts 29
cp 0.9655
rs 8.8337
c 0
b 0
f 0
cc 6
nc 7
nop 2
crap 6.0014
1
<?php
2
3
namespace AlexLisenkov\LaravelWebPush;
4
5
use AlexLisenkov\LaravelWebPush\Contracts\JWTGeneratorContract;
6
use AlexLisenkov\LaravelWebPush\Contracts\P256EncryptedMessageBuilderContract;
7
use AlexLisenkov\LaravelWebPush\Contracts\PushMessageContract;
8
use AlexLisenkov\LaravelWebPush\Contracts\PushSubscriptionContract;
9
use AlexLisenkov\LaravelWebPush\Contracts\WebPushContract;
10
use AlexLisenkov\LaravelWebPush\Exceptions\InvalidPrivateKeyException;
11
use AlexLisenkov\LaravelWebPush\Exceptions\InvalidPublicKeyException;
12
use Base64Url\Base64Url;
13
use GuzzleHttp\Client;
14
use GuzzleHttp\Promise\PromiseInterface;
15
use GuzzleHttp\Psr7\Request;
16
use Illuminate\Contracts\Config\Repository as ConfigRepository;
17
18
class WebPush implements WebPushContract
19
{
20
    /**
21
     * @var ConfigRepository
22
     */
23
    private $config_repository;
24
    /**
25
     * @var P256EncryptedMessageBuilderContract
26
     */
27
    private $encrypted_message_builder;
28
    /**
29
     * @var JWTGeneratorContract
30
     */
31
    private $JWT_generator;
32
    /**
33
     * @var Client
34
     */
35
    private $client;
36
37
    /**
38
     * WebPush constructor.
39
     *
40
     * @param ConfigRepository $config_repository
41
     * @param P256EncryptedMessageBuilderContract $encrypted_message_builder
42
     * @param JWTGeneratorContract $JWT_generator
43
     * @param Client $client
44
     */
45 16
    public function __construct(
46
        ConfigRepository $config_repository,
47
        P256EncryptedMessageBuilderContract $encrypted_message_builder,
48
        JWTGeneratorContract $JWT_generator,
49
        Client $client
50
    ) {
51 16
        $this->config_repository = $config_repository;
52 16
        $this->encrypted_message_builder = $encrypted_message_builder;
53 16
        $this->JWT_generator = $JWT_generator;
54 16
        $this->client = $client;
55 16
    }
56
57 16
    public function sendMessage(
58
        PushMessageContract $message,
59
        PushSubscriptionContract $push_subscription
60
    ): PromiseInterface {
61 16
        $private = $this->getConfigVariable('private_key');
62 16
        if (!$this->assertPrivateKeyIsCorrect($private)) {
63 1
            throw new InvalidPrivateKeyException('Configured private key is incorrect');
64
        }
65
66 15
        $public = $this->getConfigVariable('public_key');
67 15
        if (!$this->assertPublicKeyIsCorrect($public)) {
68 1
            throw new InvalidPublicKeyException('Configured public key is incorrect');
69
        }
70
71 14
        if (!$this->assertPublicKeyIsCorrect($push_subscription->getP256dh())) {
72
            throw new InvalidPublicKeyException('Subscriber public key is invalid');
73
        }
74
75 14
        $encryptedMessage = $this->encrypted_message_builder
76 14
            ->withPublicKey($push_subscription->getP256dh())
77 14
            ->withAuthToken($push_subscription->getAuth())
78 14
            ->build($message->toJson());
0 ignored issues
show
Bug introduced by Alex Lisenkov
It seems like $message->toJson() can also be of type false; however, parameter $payload of AlexLisenkov\LaravelWebP...uilderContract::build() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

78
            ->build(/** @scrutinizer ignore-type */ $message->toJson());
Loading history...
79
80 14
        $jwt = $this->JWT_generator
81 14
            ->withAudience($push_subscription->getAudience())
82 14
            ->serialize();
83
84
        $headers = [
85 14
            'Content-Type' => 'application/octet-stream',
86 14
            'Content-Encoding' => 'aesgcm',
87 14
            'Authorization' => 'WebPush ' . $jwt,
88 14
            'Encryption' => 'salt=' . $encryptedMessage->getEncodedSalt(),
89 14
            'Crypto-Key' => 'dh=' . $encryptedMessage->getEncodedPublicKey() . ';p256ecdsa=' . $this->getConfigVariable('public_key'),
90 14
            'Content-Length' => 'dh=' . $encryptedMessage->getCypherLength(),
91 14
            'TTL' => $this->getConfigVariable('TTL', Constants::DEFAULT_TTL),
92
        ];
93
94 14
        if ($topic = $message->getTopic()) {
95 1
            $headers['Topic'] = $topic;
96
        }
97
98 14
        if ($urgency = $message->getUrgency()) {
99 1
            $headers['Urgency'] = $urgency;
100
        }
101
102 14
        $request = new Request('POST', $push_subscription->getEndpoint(), $headers, $encryptedMessage->getCypher());
103
104 14
        return $this->client->sendAsync($request);
105
    }
106
107
    /**
108
     * @param string $key
109
     *
110
     * @param $default
111
     *
112
     * @return mixed
113
     */
114 16
    private function getConfigVariable(string $key, $default = null)
115
    {
116 16
        return $this->config_repository->get(Constants::CONFIG_KEY . '.' . $key, $default);
117
    }
118
119
    /**
120
     * Assert that the given private key is correct by size
121
     *
122
     * @param $private
123
     *
124
     * @return bool
125
     */
126 16
    private function assertPrivateKeyIsCorrect($private): bool
127
    {
128
        try {
129 16
            $private_key_decoded = Base64Url::decode($private);
130 1
        } catch (\InvalidArgumentException $exception) {
131 1
            return false;
132
        };
133
134 15
        return mb_strlen($private_key_decoded, '8bit') === 32;
135
    }
136
137
    /**
138
     * Assert that the given public key is correct by size
139
     *
140
     * @param $public
141
     *
142
     * @return bool
143
     */
144 15
    private function assertPublicKeyIsCorrect($public): bool
145
    {
146
        try {
147 15
            $public_key_decoded = Base64Url::decode($public);
148 1
        } catch (\InvalidArgumentException $exception) {
149 1
            return false;
150
        };
151
152 14
        return mb_strlen($public_key_decoded, '8bit') === 65;
153
    }
154
155
}
156