Passed
Pull Request — master (#2)
by Tharanga
02:42
created

getDecodedData()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5.675

Importance

Changes 0
Metric Value
cc 5
eloc 9
nc 4
nop 0
dl 0
loc 17
ccs 7
cts 10
cp 0.7
crap 5.675
rs 9.6111
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Tharanga Kothalawala <[email protected]>
4
 * @date 30-12-2018
5
 */
6
7
namespace TSK\SSO\Storage;
8
9
use TSK\SSO\AppUser\AppUser;
10
use TSK\SSO\Storage\Exception\DataCannotBeStoredException;
11
use TSK\SSO\ThirdParty\CommonAccessToken;
12
use TSK\SSO\ThirdParty\ThirdPartyUser;
13
14
/**
15
 * @package TSK\SSO\Storage
16
 *
17
 * This will use the server file system to persist vendor data.
18
 */
19
class FileSystemThirdPartyStorageRepository implements ThirdPartyStorageRepository
20
{
21
    const FILE_NAME = 'tsk.sso.storage.json';
22
23
    /**
24
     * @var string
25
     */
26
    private $baseDirectory;
27
28
    /**
29
     * @param string $baseDirectory
30
     */
31 4
    public function __construct($baseDirectory = '/tmp')
32
    {
33 4
        $this->baseDirectory = $baseDirectory;
34 4
        $this->createFile();
35 4
    }
36
37
    /**
38
     * Returns a mapped full user record for a given vendor email address or null.
39
     *
40
     * @param string $emailAddress email address used within the given vendor system
41
     * @param string|null $vendorName [optional] name of the third party vendor for vendor filtering
42
     * @return MappedUser|null
43
     */
44 2
    public function getUser($emailAddress, $vendorName = null)
45
    {
46 2
        $fileDataDecoded = $this->getDecodedData();
47 2
        if (is_null($fileDataDecoded)) {
48
            return null;
49
        }
50
51 2
        foreach ($fileDataDecoded as $vendorAndEmail => $mappedUser) {
52 2
            if (is_null($vendorName)) {
53 2
                $vendorName = $mappedUser['vendor_name'];
54 2
            }
55
56 2
            $searchKey = sprintf('%s::%s', $vendorName, $emailAddress);
57 2
            if ($vendorAndEmail !== $searchKey) {
58 1
                continue;
59
            }
60
61 2
            return new MappedUser(
62 2
                $mappedUser['app_user_id'],
63 2
                $mappedUser['vendor_name'],
64 2
                $mappedUser['vendor_email'],
65 2
                $mappedUser['vendor_access_token'],
66 2
                $mappedUser['vendor_data']
67 2
            );
68 2
        }
69
70 2
        return null;
71
    }
72
73
    /**
74
     * Returns any vendor MappedUser list for a given Application user.
75
     * Use this to display all the vendor connections.
76
     *
77
     * @param AppUser $appUser
78
     * @return MappedUser[]
79
     */
80 1
    public function getByAppUser(AppUser $appUser)
81
    {
82 1
        $fileDataDecoded = $this->getDecodedData();
83 1
        if (is_null($fileDataDecoded)) {
84
            return array();
85
        }
86
87 1
        $connections = array();
88 1
        foreach ($fileDataDecoded as $vendorAndEmail => $mappedUser) {
89 1
            if ($mappedUser['app_user_id'] != $appUser->id()) {
90 1
                continue;
91
            }
92
93 1
            $connections[] = new MappedUser(
94 1
                $mappedUser['app_user_id'],
95 1
                $mappedUser['vendor_name'],
96 1
                $mappedUser['vendor_email'],
97 1
                $mappedUser['vendor_access_token'],
98 1
                $mappedUser['vendor_data']
99 1
            );
100 1
        }
101
102 1
        return $connections;
103
    }
104
105
    /**
106
     * @param AppUser $appUser
107
     * @param ThirdPartyUser $thirdPartyUser
108
     * @param CommonAccessToken $accessToken
109
     * @throws DataCannotBeStoredException
110
     */
111 1
    public function save(
112
        AppUser $appUser,
113
        ThirdPartyUser $thirdPartyUser,
114
        CommonAccessToken $accessToken
115
    ) {
116 1
        $data = $this->getDecodedData();
117 1
        if (is_null($data)) {
118
            return;
119
        }
120
121 1
        $key = sprintf('%s::%s', $accessToken->vendor(), $thirdPartyUser->email());
122 1
        $data[$key] = array(
123 1
            'app_user_id' => $appUser->id(),
124 1
            'vendor_name' => $accessToken->vendor(),
125 1
            'vendor_email' => $thirdPartyUser->email(),
126 1
            'vendor_access_token' => $accessToken->token(),
127 1
            'vendor_data' => json_encode($thirdPartyUser->toArray()),
128 1
            'created_at' => date('Y-m-d H:i:00'),
129
        );
130
131 1
        $written = file_put_contents($this->fileAbsolutePath(), json_encode($data));
132 1
        if (!$written) {
133
            throw new DataCannotBeStoredException("Couldn't save the third party user data due to a file system error");
134
        }
135 1
    }
136
137
    /**
138
     * Use this to clean the storage after revoking access to a third party for example.
139
     *
140
     * @param string $emailAddress email address used within the given vendor system
141
     * @param string $vendorName name of the third party where the given email belongs to
142
     * @return bool
143
     */
144 1
    public function remove($emailAddress, $vendorName)
145
    {
146 1
        $data = $this->getDecodedData();
147 1
        if (is_null($data)) {
148
            return false;
149
        }
150
151 1
        $key = sprintf('%s::%s', $vendorName, $emailAddress);
152 1
        unset($data[$key]);
153 1
        return file_put_contents($this->fileAbsolutePath(), json_encode($data));
0 ignored issues
show
Bug Best Practice introduced by
The expression return file_put_contents...(), json_encode($data)) returns the type integer which is incompatible with the documented return type boolean.
Loading history...
154
    }
155
156
    /**
157
     * Returns an array containing raw third party user mappings.
158
     *
159
     * @return array|null
160
     */
161 4
    protected function getDecodedData()
162
    {
163 4
        $fileData = file_get_contents($this->fileAbsolutePath());
164 4
        if (empty($fileData)) {
165
            return null;
166
        }
167
168 4
        $fileDataDecoded = @json_decode($fileData, true);
169 4
        if ($fileDataDecoded === null && json_last_error() !== JSON_ERROR_NONE) {
170
            return null;
171
        }
172
173 4
        if (!is_array($fileDataDecoded)) {
174
            return null;
175
        }
176
177 4
        return $fileDataDecoded;
178
    }
179
180
    /**
181
     * Creates a new storage file in the file system. empty json object will be added if the file is empty.
182
     */
183 4
    private function createFile()
184
    {
185 4
        if (!file_exists($this->fileAbsolutePath())) {
186 1
            file_put_contents($this->fileAbsolutePath(), '{}');
187 1
        }
188
189 4
        $contents = file_get_contents($this->fileAbsolutePath());
190 4
        if (empty($contents)) {
191
            file_put_contents($this->fileAbsolutePath(), '{}');
192
        }
193 4
    }
194
195
    /**
196
     * @return string
197
     */
198 4
    private function fileAbsolutePath()
199
    {
200 4
        return sprintf('%s/%s', $this->baseDirectory, self::FILE_NAME);
201
    }
202
}
203