Completed
Push — master ( d7e260...fc7aea )
by Artem
10:19
created

ImportsUsers::parseDump()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 30
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 3
dl 0
loc 30
ccs 0
cts 13
cp 0
crap 12
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace Slides\Connector\Auth\Sync;
4
5
use Illuminate\Encryption\Encrypter;
6
7
/**
8
 * Trait ImportsUsers
9
 *
10
 * @package Slides\Connector\Auth\Sync
11
 */
12
trait ImportsUsers
13
{
14
    /**
15
     * The import dump headers.
16
     *
17
     * @var array
18
     */
19
    protected $importHeaders;
20
21
    /**
22
     * Export local users to a file in the compressed GZIP format.
23
     *
24
     * @param string $path
25
     * @param string $sharedKey
26
     * @param bool $importModes Whether import modes from the dump.
27
     *
28
     * @return void
29
     */
30
    public function import(string $path, string $sharedKey, bool $importModes = true)
31
    {
32
        $difference = $this->parseDump($path, $sharedKey, $importModes);
33
34
        $foreigners = array_map(function (array $user) {
35
            return $this->createRemoteUserFromResponse($user);
0 ignored issues
show
Bug introduced by
It seems like createRemoteUserFromResponse() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

35
            return $this->/** @scrutinizer ignore-call */ createRemoteUserFromResponse($user);
Loading history...
36
        }, $difference);
37
38
        $this->foreigners = collect($foreigners);
0 ignored issues
show
Bug Best Practice introduced by
The property foreigners does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
39
    }
40
41
    /**
42
     * Parse a dump and retrieve user entities in array representation.
43
     *
44
     * Steps:
45
     * 1. Read a file contents
46
     * 2. Decrypt using shared token and tenant credentials
47
     * 3. Decompress and decode from JSON to array
48
     *
49
     * @param string $filename
50
     * @param string $sharedKey
51
     * @param bool $importModes Whether import modes from the dump.
52
     *
53
     * @return array
54
     */
55
    private function parseDump(string $filename, string $sharedKey, bool $importModes): array
56
    {
57
        if(!file_exists($filename)) {
58
            throw new \InvalidArgumentException($filename . ' cannot be found');
59
        }
60
61
        // Retrieve dump contents, it's encrypted and gzipped
62
        // We should handle it firstly
63
        $payload = file_get_contents($filename);
64
65
        // Extract and parse headers which contain necessary information
66
        // Then delete from a dump so we can proceed with decryption
67
        $this->extractHeaders($payload);
68
69
        if($importModes) {
70
            $this->modes = array_get($this->importHeaders, 'modes', []);
0 ignored issues
show
Bug Best Practice introduced by
The property modes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
71
        }
72
73
        $encrypter = new Encrypter(
74
            $this->createDecryptionKey($sharedKey),
75
            $this->cipher
76
        );
77
78
        // Decrypt a payload. If successful, we retrieve compressed user entities
79
        $data = $encrypter->decrypt($payload);
80
81
        // Decompress users
82
        $data = gzdecode($data);
83
84
        return json_decode($data, true);
85
    }
86
87
    /**
88
     * Create a decryption key based on shared key and signature which authorizes tenant.
89
     *
90
     * @param string $sharedKey
91
     *
92
     * @return string
93
     */
94
    private function createDecryptionKey(string $sharedKey): string
95
    {
96
        return base64_decode($sharedKey) . ':' . $this->signature();
97
    }
98
99
    /**
100
     * Create a signature based on credentials.
101
     *
102
     * @return string
103
     */
104
    private function signature(): string
105
    {
106
        $sign = hash('sha256', $this->credential('public') . $this->credential('secret'));
0 ignored issues
show
Bug introduced by
It seems like credential() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

106
        $sign = hash('sha256', $this->/** @scrutinizer ignore-call */ credential('public') . $this->credential('secret'));
Loading history...
107
108
        return substr($sign, 0, 15);
109
    }
110
111
    /**
112
     * Extract and parse headers from the dump.
113
     *
114
     * @param string $payload
115
     *
116
     * @return array
117
     */
118
    private function extractHeaders(string &$payload): array
119
    {
120
        $headers = unserialize(
121
            base64_decode(strtok($payload, '/'))
122
        );
123
124
        // Remove header string from the payload
125
        $payload = ltrim(strstr($payload, '/'), '/');
126
127
        return $this->importHeaders = $headers;
128
    }
129
}