Test Failed
Pull Request — master (#1)
by Alex
02:01
created

WebPush::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 4
crap 1
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 2
    public function __construct(
46
        ConfigRepository $config_repository,
47
        P256EncryptedMessageBuilderContract $encrypted_message_builder,
48
        JWTGeneratorContract $JWT_generator,
49
        Client $client
50
    ) {
51 2
        $this->config_repository = $config_repository;
52 2
        $this->encrypted_message_builder = $encrypted_message_builder;
53 2
        $this->JWT_generator = $JWT_generator;
54 2
        $this->client = $client;
55 2
    }
56
57 2
    public function sendMessage(
58
        PushMessageContract $message,
59
        PushSubscriptionContract $push_subscription
60
    ): PromiseInterface {
61 2
        $private = $this->getConfigVariable('private_key');
62 2
        if (!$this->assertPrivateKeyIsCorrect($private)) {
63 1
            throw new InvalidPrivateKeyException('Configured private key is incorrect');
64
        }
65
66 1
        $public = $this->getConfigVariable('public_key');
67 1
        if (!$this->assertPublicKeyIsCorrect($public)) {
68 1
            throw new InvalidPublicKeyException('Configured public key is incorrect');
69
        }
70
71
        if (!$this->assertPublicKeyIsCorrect($push_subscription->getP256dh())) {
72
            throw new InvalidPublicKeyException('Subscriber public key is invalid');
73
        }
74
75
        $encryptedMessage = $this->encrypted_message_builder
76
            ->withPublicKey($push_subscription->getP256dh())
77
            ->withAuthToken($push_subscription->getAuth())
78
            ->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

78
            ->build(/** @scrutinizer ignore-type */ $message->toJson());
Loading history...
79
80
        $jwt = $this->JWT_generator
81
            ->withAudience($push_subscription->getAudience())
82
            ->serialize();
83
84
        $headers = [
85
            'Content-Type' => 'application/octet-stream',
86
            'Content-Encoding' => 'aesgcm',
87
            'Authorization' => 'WebPush ' . $jwt,
88
            'Encryption' => 'salt=' . $encryptedMessage->getEncodedSalt(),
89
            'Crypto-Key' => 'dh=' . $encryptedMessage->getEncodedPublicKey() . ';p256ecdsa=' . $this->getConfigVariable('public_key'),
90
            'Content-Length' => 'dh=' . $encryptedMessage->getCypherLength(),
91
            'TTL' => $this->getConfigVariable('TTL', Constants::DEFAULT_TTL),
92
        ];
93
94
        if ($topic = $message->getTopic()) {
95
            $headers['Topic'] = $topic;
96
        }
97
98
        if ($urgency = $message->getUrgency()) {
99
            $headers['Urgency'] = $urgency;
100
        }
101
102
        $request = new Request('POST', $push_subscription->getEndpoint(), $headers, $encryptedMessage->getCypher());
103
104
        return $this->client->sendAsync($request);
105
    }
106
107
    /**
108
     * @param string $key
109
     *
110
     * @param $default
111
     *
112
     * @return mixed
113
     */
114 2
    private function getConfigVariable(string $key, $default = null)
115
    {
116 2
        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 2
    private function assertPrivateKeyIsCorrect($private): bool
127
    {
128
        try {
129 2
            $private_key_decoded = Base64Url::decode($private);
130 1
        } catch (\InvalidArgumentException $exception) {
131 1
            return false;
132
        };
133
134 1
        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 1
    private function assertPublicKeyIsCorrect($public): bool
145
    {
146
        try {
147 1
            $public_key_decoded = Base64Url::decode($public);
148 1
        } catch (\InvalidArgumentException $exception) {
149 1
            return false;
150
        };
151
152
        return mb_strlen($public_key_decoded, '8bit') === 32;
153
    }
154
155
}
156