Completed
Push — master ( df062e...3563b3 )
by Tharanga
02:22
created

PeclMongoDbThirdPartyStorageRepository::save()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 46
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 31
nc 4
nop 3
dl 0
loc 46
rs 9.424
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Tharanga Kothalawala <[email protected]>
4
 * @date 27-01-2019
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
use MongoDB\BSON\ObjectID;
14
use MongoDB\Driver\BulkWrite;
15
use MongoDB\Driver\Exception\Exception;
16
use MongoDB\Driver\Exception\InvalidArgumentException;
17
use MongoDB\Driver\Manager as MongoManager;
18
use MongoDB\Driver\Query;
19
20
/**
21
 * @codeCoverageIgnore
22
 * @package TSK\SSO\Storage
23
 *
24
 * This will connect to a MongoDB database using the pecl/mongodb driver to persist vendor data.
25
 * The collection will be created on the fly.
26
 */
27
class PeclMongoDbThirdPartyStorageRepository implements ThirdPartyStorageRepository
28
{
29
    /**
30
     * @var MongoManager
31
     */
32
    private $mongoManager;
33
34
    /**
35
     * @var string name of the database in the MongoDB server
36
     */
37
    private $dbName;
38
39
    /**
40
     * @var string name of the collection in the application database
41
     */
42
    private $collection;
43
44
    /**
45
     * @param MongoManager $mongoManager
46
     * @param string $dbName
47
     * @param string $collection [optional] name of the collection in the MongoDB database
48
     */
49
    public function __construct(MongoManager $mongoManager, $dbName, $collection = 'thirdparty_connections')
50
    {
51
        $this->mongoManager = $mongoManager;
52
        $this->dbName = $dbName;
53
        $this->collection = $collection;
54
    }
55
56
    /**
57
     * Returns a mapped full user record for a given vendor email address or null.
58
     *
59
     * @param string $emailAddress email address used within the given vendor system
60
     * @param string|null $vendorName [optional] name of the third party vendor for vendor filtering
61
     * @return MappedUser|null
62
     */
63
    public function getUser($emailAddress, $vendorName = null)
64
    {
65
        if (is_null($vendorName)) {
66
            $cursor = $this->mongoManager->executeQuery(
67
                $this->collectionName(),
68
                new Query(array(
69
                    'vendor_email' => $emailAddress,
70
                ))
71
            );
72
        } else {
73
            $cursor = $this->mongoManager->executeQuery(
74
                $this->collectionName(),
75
                new Query(array(
76
                    'vendor_email' => $emailAddress,
77
                    'vendor_name' => $vendorName,
78
                ))
79
            );
80
        }
81
82
        foreach ($cursor as $mappedUserDoc) {
83
            return new MappedUser(
84
                $mappedUserDoc->app_user_id,
85
                $mappedUserDoc->vendor_name,
86
                $mappedUserDoc->vendor_email,
87
                $mappedUserDoc->vendor_access_token,
88
                $mappedUserDoc->vendor_data
89
            );
90
        }
91
92
        return null;
93
    }
94
95
    /**
96
     * Returns any vendor MappedUser list for a given Application user.
97
     *
98
     * @param AppUser $appUser
99
     * @return MappedUser[]
100
     * @throws InvalidArgumentException
101
     */
102
    public function getByAppUser(AppUser $appUser)
103
    {
104
        $cursor = $this->mongoManager->executeQuery(
105
            $this->collectionName(),
106
            new Query(array('app_user_id' => $appUser->id()))
107
        );
108
109
        $connections = array();
110
        foreach ($cursor as $mappedUserDoc) {
111
            $connections[] = new MappedUser(
112
                $mappedUserDoc->app_user_id,
113
                $mappedUserDoc->vendor_name,
114
                $mappedUserDoc->vendor_email,
115
                $mappedUserDoc->vendor_access_token,
116
                $mappedUserDoc->vendor_data
117
            );
118
        }
119
120
        return $connections;
121
    }
122
123
    /**
124
     * @param AppUser $appUser
125
     * @param ThirdPartyUser $thirdPartyUser
126
     * @param CommonAccessToken $accessToken
127
     * @throws DataCannotBeStoredException
128
     */
129
    public function save(
130
        AppUser $appUser,
131
        ThirdPartyUser $thirdPartyUser,
132
        CommonAccessToken $accessToken
133
    ) {
134
        $isUserHasConnection = $this->isUserHasConnection(
135
            $appUser->id(),
136
            $accessToken->vendor(),
137
            $thirdPartyUser->email()
138
        );
139
140
        $bulk = new BulkWrite();
141
142
        if ($isUserHasConnection) {
143
            $bulk->update(
144
                array(
145
                    'app_user_id' => $appUser->id(),
146
                    'vendor_name' => $thirdPartyUser->email(),
147
                    'vendor_email' => $accessToken->token(),
148
                ),
149
                array(
150
                    '$set' => array(
151
                        'vendor_access_token' => $accessToken->token(),
152
                        'vendor_data' => json_encode($thirdPartyUser->toArray()),
153
                    ),
154
                )
155
            );
156
        } else {
157
            $bulk->insert(array(
158
                '_id' => new ObjectID(),
159
                'app_user_id' => $appUser->id(),
160
                'vendor_name' => $accessToken->vendor(),
161
                'vendor_email' => $thirdPartyUser->email(),
162
                'vendor_access_token' => $accessToken->token(),
163
                'vendor_data' => json_encode($thirdPartyUser->toArray()),
164
                'created_at' => date('Y-m-d H:i:s'),
165
            ));
166
        }
167
168
        try {
169
            $this->mongoManager->executeBulkWrite($this->collectionName(), $bulk);
170
        } catch (Exception $ex) {
171
            throw new DataCannotBeStoredException(
172
                sprintf("Couldn't save the third party user data. Error : %s", $ex->getMessage()),
173
                $ex->getCode(),
174
                $ex
175
            );
176
        }
177
    }
178
179
    /**
180
     * Use this to clean the storage after revoking access to a third party for example.
181
     *
182
     * @param string $emailAddress email address used within the given vendor system
183
     * @param string $vendorName name of the third party where the given email belongs to
184
     * @return bool
185
     */
186
    public function remove($emailAddress, $vendorName)
187
    {
188
        $bulk = new BulkWrite();
189
        $bulk->delete(array('vendor_email' => $emailAddress, 'vendor_name' => $vendorName));
190
191
        $writeResult = $this->mongoManager->executeBulkWrite($this->collectionName(), $bulk);
192
193
        return $writeResult->getDeletedCount() === 1;
194
    }
195
196
    /**
197
     * Check if the given user has a mapping to a requested vendor connection.
198
     *
199
     * @param string $appUserId
200
     * @param string $vendorName
201
     * @param string $vendorEmail
202
     * @return bool
203
     */
204
    private function isUserHasConnection($appUserId, $vendorName, $vendorEmail)
205
    {
206
        $cursor = $this->mongoManager->executeQuery(
207
            $this->collectionName(),
208
            new Query(array(
209
                'app_user_id' => $appUserId,
210
                'vendor_name' => $vendorName,
211
                'vendor_email' => $vendorEmail,
212
            ))
213
        );
214
215
        foreach ($cursor as $_) {
216
            return true;
217
        }
218
219
        return false;
220
    }
221
222
    /**
223
     * Returns a namespaced collection name.
224
     *
225
     * @return string
226
     */
227
    private function collectionName()
228
    {
229
        return sprintf('%s.%s', $this->dbName, $this->collection);
230
    }
231
}
232