Completed
Pull Request — master (#1)
by Alex
04:28
created

WebPush::sendMessage()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 6.0397

Importance

Changes 0
Metric Value
eloc 29
dl 0
loc 49
c 0
b 0
f 0
ccs 26
cts 29
cp 0.8966
rs 8.8337
cc 6
nc 7
nop 2
crap 6.0397
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
63 16
        if (!$this->assertPrivateKeyIsCorrect($private)) {
64
            throw new InvalidPrivateKeyException('Configured private key is incorrect');
65
        }
66
67 15
        $public = $this->getConfigVariable('public_key');
68 15
        if (!$this->assertPublicKeyIsCorrect($public)) {
69
            throw new InvalidPublicKeyException('Configured public key is incorrect');
70
        }
71
72 14
        if (!$this->assertPublicKeyIsCorrect($push_subscription->getP256dh())) {
73
            throw new InvalidPublicKeyException('Subscriber public key is invalid');
74
        }
75
76 14
        $encryptedMessage = $this->encrypted_message_builder
77 14
            ->withPublicKey($push_subscription->getP256dh())
78 14
            ->withAuthToken($push_subscription->getAuth())
79 14
            ->build($message->toJson());
0 ignored issues
show
Bug introduced by
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

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