1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* ClientService.php |
4
|
|
|
* |
5
|
|
|
* PHP version 5.6+ |
6
|
|
|
* |
7
|
|
|
* @author Philippe Gaultier <[email protected]> |
8
|
|
|
* @copyright 2010-2017 Philippe Gaultier |
9
|
|
|
* @license http://www.sweelix.net/license license |
10
|
|
|
* @version 1.2.0 |
11
|
|
|
* @link http://www.sweelix.net |
12
|
|
|
* @package sweelix\oauth2\server\services\mySql |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
namespace sweelix\oauth2\server\services\mySql; |
16
|
|
|
|
17
|
|
|
use sweelix\oauth2\server\exceptions\DuplicateIndexException; |
18
|
|
|
use sweelix\oauth2\server\exceptions\DuplicateKeyException; |
19
|
|
|
use sweelix\oauth2\server\interfaces\ClientModelInterface; |
20
|
|
|
use sweelix\oauth2\server\interfaces\ClientServiceInterface; |
21
|
|
|
use yii\db\Exception as DatabaseException; |
22
|
|
|
use Yii; |
23
|
|
|
use yii\db\Query; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* This is the client service for mySql |
27
|
|
|
* |
28
|
|
|
* @author Philippe Gaultier <[email protected]> |
29
|
|
|
* @copyright 2010-2017 Philippe Gaultier |
30
|
|
|
* @license http://www.sweelix.net/license license |
31
|
|
|
* @version 1.2.0 |
32
|
|
|
* @link http://www.sweelix.net |
33
|
|
|
* @package sweelix\oauth2\server\services\mySql |
34
|
|
|
* @since 1.0.0 |
35
|
|
|
*/ |
36
|
|
|
class ClientService extends BaseService implements ClientServiceInterface |
37
|
|
|
{ |
38
|
|
|
/** |
39
|
|
|
* @var string sql client grantType junction table |
40
|
|
|
*/ |
41
|
|
|
public $clientGrantTypeTable = null; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var string sql clients table |
45
|
|
|
*/ |
46
|
|
|
public $clientsTable = null; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var string sql client user table |
50
|
|
|
*/ |
51
|
|
|
public $clientUserTable = null; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var string sql scope client junction table |
55
|
|
|
*/ |
56
|
|
|
public $scopeClientTable = null; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var string sql scopes table |
60
|
|
|
*/ |
61
|
|
|
public $scopesTable = null; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Save Client |
65
|
|
|
* @param ClientModelInterface $client |
66
|
|
|
* @param null|array $attributes attributes to save |
67
|
|
|
* @return bool |
68
|
|
|
* @throws DatabaseException |
69
|
|
|
* @throws DuplicateIndexException |
70
|
|
|
* @throws DuplicateKeyException |
71
|
|
|
* @since 1.0.0 |
72
|
|
|
*/ |
73
|
|
|
protected function insert(ClientModelInterface $client, $attributes) |
74
|
|
|
{ |
75
|
|
|
$result = false; |
76
|
|
|
if (!$client->beforeSave(true)) { |
77
|
|
|
return $result; |
78
|
|
|
} |
79
|
|
|
$clientKey = $client->getKey(); |
80
|
|
|
$entity = (new Query()) |
81
|
|
|
->select('*') |
82
|
|
|
->from($this->clientsTable) |
83
|
|
|
->where('id = :id', [':id' => $clientKey]) |
84
|
|
|
->one($this->db); |
85
|
|
|
if ($entity !== false) { |
86
|
|
|
throw new DuplicateKeyException('Duplicate key "' . $clientKey . '"'); |
87
|
|
|
} |
88
|
|
|
$values = $client->getDirtyAttributes($attributes); |
|
|
|
|
89
|
|
|
$clientParameters = []; |
90
|
|
|
$this->setAttributesDefinitions($client->attributesDefinition()); |
91
|
|
|
foreach ($values as $key => $value) { |
92
|
|
|
if (($value !== null) && ($key !== 'grantTypes') && ($key !== 'scopes')) { |
93
|
|
|
$clientParameters[$key] = $this->convertToDatabase($key, $value); |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
$clientParameters['dateCreated'] = date('Y-m-d H:i:s'); |
97
|
|
|
$clientParameters['dateUpdated'] = date('Y-m-d H:i:s'); |
98
|
|
|
try { |
99
|
|
|
$this->db->createCommand() |
100
|
|
|
->insert($this->clientsTable, $clientParameters) |
101
|
|
|
->execute(); |
102
|
|
|
if (!empty($values['grantTypes'])) { |
103
|
|
|
$values['grantTypes'] = array_unique($values['grantTypes']); |
104
|
|
|
foreach ($values['grantTypes'] as $grantType) { |
105
|
|
|
$clientGrantTypeParams = [ |
106
|
|
|
'clientId' => $clientKey, |
107
|
|
|
'grantTypeId' => $grantType, |
108
|
|
|
]; |
109
|
|
|
$this->db->createCommand() |
110
|
|
|
->insert($this->clientGrantTypeTable, $clientGrantTypeParams) |
111
|
|
|
->execute(); |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
if (!empty($values['scopes'])) { |
115
|
|
|
$values['scopes'] = array_unique($values['scopes']); |
116
|
|
|
foreach ($values['scopes'] as $scope) { |
117
|
|
|
$scopeClientParams = [ |
118
|
|
|
'scopeId' => $scope, |
119
|
|
|
'clientId' => $clientKey |
120
|
|
|
]; |
121
|
|
|
$this->db->createCommand() |
122
|
|
|
->insert($this->scopeClientTable, $scopeClientParams) |
123
|
|
|
->execute(); |
124
|
|
|
} |
125
|
|
|
} |
126
|
|
|
} catch (DatabaseException $e) { |
127
|
|
|
// @codeCoverageIgnoreStart |
128
|
|
|
// we have a MYSQL exception, we should not discard |
129
|
|
|
Yii::debug('Error while inserting entity', __METHOD__); |
130
|
|
|
throw $e; |
131
|
|
|
// @codeCoverageIgnoreEnd |
132
|
|
|
} |
133
|
|
|
$changedAttributes = array_fill_keys(array_keys($values), null); |
134
|
|
|
$client->setOldAttributes($values); |
135
|
|
|
$client->afterSave(true, $changedAttributes); |
136
|
|
|
$result = true; |
137
|
|
|
return $result; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Update Client |
142
|
|
|
* @param ClientModelInterface $client |
143
|
|
|
* @param null|array $attributes attributes to save |
144
|
|
|
* @return bool |
145
|
|
|
* @throws DatabaseException |
146
|
|
|
* @throws DuplicateIndexException |
147
|
|
|
* @throws DuplicateKeyException |
148
|
|
|
*/ |
149
|
|
|
protected function update(ClientModelInterface $client, $attributes) |
150
|
|
|
{ |
151
|
|
|
if (!$client->beforeSave(false)) { |
152
|
|
|
return false; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
$values = $client->getDirtyAttributes($attributes); |
|
|
|
|
156
|
|
|
$modelKey = $client->key(); |
157
|
|
|
if (isset($values[$modelKey]) === true) { |
158
|
|
|
$entity = (new Query()) |
159
|
|
|
->select('*') |
160
|
|
|
->from($this->clientsTable) |
161
|
|
|
->where('id = :id', [':id' => $values[$modelKey]]) |
162
|
|
|
->one($this->db); |
163
|
|
|
if ($entity !== false) { |
164
|
|
|
throw new DuplicateKeyException('Duplicate key "' . $values[$modelKey] . '"'); |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
$clientKey = isset($values[$modelKey]) ? $values[$modelKey] : $client->getKey(); |
168
|
|
|
|
169
|
|
|
$clientParameters = []; |
170
|
|
|
$this->setAttributesDefinitions($client->attributesDefinition()); |
171
|
|
|
foreach ($values as $key => $value) { |
172
|
|
|
if (($key !== 'grantTypes') && ($key !== 'scopes')) { |
173
|
|
|
$clientParameters[$key] = ($value !== null) ? $this->convertToDatabase($key, $value) : null; |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
$clientParameters['dateUpdated'] = date('Y-m-d H:i:s'); |
177
|
|
|
try { |
178
|
|
|
if (array_key_exists($modelKey, $values) === true) { |
179
|
|
|
$oldClientKey = $client->getOldKey(); |
180
|
|
|
$this->db->createCommand() |
181
|
|
|
->update($this->clientsTable, $clientParameters, 'id = :id', [':id' => $oldClientKey]) |
182
|
|
|
->execute(); |
183
|
|
|
} else { |
184
|
|
|
$this->db->createCommand() |
185
|
|
|
->update($this->clientsTable, $clientParameters, 'id = :id', [':id' => $clientKey]) |
186
|
|
|
->execute(); |
187
|
|
|
} |
188
|
|
|
if (isset($values['grantTypes'])) { |
189
|
|
|
$values['grantTypes'] = array_unique($values['grantTypes']); |
190
|
|
|
$clientGrantTypes = (new Query()) |
191
|
|
|
->select('*') |
192
|
|
|
->from($this->clientGrantTypeTable) |
193
|
|
|
->where('clientId = :clientId', [':clientId' => $clientKey]) |
194
|
|
|
->all($this->db); |
195
|
|
|
foreach ($clientGrantTypes as $clientGrantType) { |
196
|
|
|
if (($index = array_search($clientGrantType['grantTypeId'], $values['grantTypes'])) === false) { |
197
|
|
|
$this->db->createCommand() |
198
|
|
|
->delete($this->clientGrantTypeTable, |
199
|
|
|
['clientId = :clientId', 'grantTypeId = :grantTypeId'], |
200
|
|
|
[':clientId' => $clientKey, ':grantTypeId' => $clientGrantType['grantTypeId']]) |
201
|
|
|
->execute(); |
202
|
|
|
} else { |
203
|
|
|
unset($values['grantTypes'][$index]); |
204
|
|
|
} |
205
|
|
|
} |
206
|
|
|
foreach ($values['grantTypes'] as $grantType) { |
207
|
|
|
$clientGrantTypeParams = [ |
208
|
|
|
'clientId' => $clientKey, |
209
|
|
|
'grantTypeId' => $grantType, |
210
|
|
|
]; |
211
|
|
|
$this->db->createCommand() |
212
|
|
|
->insert($this->clientGrantTypeTable, $clientGrantTypeParams) |
213
|
|
|
->execute(); |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
if (isset($values['scopes'])) { |
217
|
|
|
$values['scopes'] = array_unique($values['scopes']); |
218
|
|
|
$scopeClients = (new Query()) |
219
|
|
|
->select('*') |
220
|
|
|
->from($this->scopeClientTable) |
221
|
|
|
->where('clientId = :clientId', [':clientId' => $clientKey]) |
222
|
|
|
->all($this->db); |
223
|
|
|
foreach ($scopeClients as $scopeClient) { |
224
|
|
|
if (($index = array_search($scopeClient['scopeId'], $values['scopes'])) === false) { |
225
|
|
|
$this->db->createCommand() |
226
|
|
|
->delete($this->scopeClientTable, |
227
|
|
|
'clientId = :clientId AND scopeId = :scopeId', |
228
|
|
|
[':clientId' => $clientKey, ':scopeId' => $scopeClient['scopeId']]) |
229
|
|
|
->execute(); |
230
|
|
|
} else { |
231
|
|
|
unset($values['scopes'][$index]); |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
foreach ($values['scopes'] as $scope) { |
235
|
|
|
$scopeClientParams = [ |
236
|
|
|
'scopeId' => $scope, |
237
|
|
|
'clientId' => $clientKey |
238
|
|
|
]; |
239
|
|
|
$this->db->createCommand() |
240
|
|
|
->insert($this->scopeClientTable, $scopeClientParams) |
241
|
|
|
->execute(); |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
} catch (DatabaseException $e) { |
246
|
|
|
// @codeCoverageIgnoreStart |
247
|
|
|
// we have a MYSQL exception, we should not discard |
248
|
|
|
Yii::debug('Error while updating entity', __METHOD__); |
249
|
|
|
throw $e; |
250
|
|
|
// @codeCoverageIgnoreEnd |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
$changedAttributes = []; |
254
|
|
|
foreach ($values as $name => $value) { |
255
|
|
|
$oldAttributes = $client->getOldAttributes(); |
256
|
|
|
$changedAttributes[$name] = isset($oldAttributes[$name]) ? $oldAttributes[$name] : null; |
257
|
|
|
$client->setOldAttribute($name, $value); |
258
|
|
|
} |
259
|
|
|
$client->afterSave(false, $changedAttributes); |
260
|
|
|
return true; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* @inheritdoc |
265
|
|
|
*/ |
266
|
|
|
public function save(ClientModelInterface $client, $attributes) |
267
|
|
|
{ |
268
|
|
|
if ($client->getIsNewRecord()) { |
269
|
|
|
$result = $this->insert($client, $attributes); |
270
|
|
|
} else { |
271
|
|
|
$result = $this->update($client, $attributes); |
272
|
|
|
} |
273
|
|
|
return $result; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* @inheritdoc |
278
|
|
|
*/ |
279
|
|
|
public function findOne($key) |
280
|
|
|
{ |
281
|
|
|
$record = null; |
282
|
|
|
$clientData = (new Query()) |
283
|
|
|
->select('*') |
284
|
|
|
->from($this->clientsTable) |
285
|
|
|
->where('id = :id', [':id' => $key]) |
286
|
|
|
->one($this->db); |
287
|
|
|
|
288
|
|
|
if ($clientData !== false) { |
289
|
|
|
$clientData['grantTypes'] = []; |
290
|
|
|
$clientData['scopes'] = []; |
291
|
|
|
$tmpGrantTypes = (new Query()) |
292
|
|
|
->select('grantTypeId') |
293
|
|
|
->from($this->clientGrantTypeTable) |
294
|
|
|
->all($this->db); |
295
|
|
|
foreach ($tmpGrantTypes as $grantType) { |
296
|
|
|
$clientData['grantTypes'][] = $grantType['grantTypeId']; |
297
|
|
|
} |
298
|
|
|
$tmpScopes = (new Query()) |
299
|
|
|
->select('scopeId') |
300
|
|
|
->from($this->scopeClientTable) |
301
|
|
|
->all($this->db); |
302
|
|
|
foreach ($tmpScopes as $scope) { |
303
|
|
|
$clientData['scopes'][] = $scope['scopeId']; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
$record = Yii::createObject('sweelix\oauth2\server\interfaces\ClientModelInterface'); |
307
|
|
|
/** @var ClientModelInterface $record */ |
308
|
|
|
$properties = $record->attributesDefinition(); |
309
|
|
|
$this->setAttributesDefinitions($properties); |
310
|
|
|
$attributes = []; |
311
|
|
|
foreach ($clientData as $key => $value) { |
312
|
|
|
if (isset($properties[$key]) === true) { |
313
|
|
|
$clientData[$key] = $this->convertToModel($key, $value); |
314
|
|
|
$record->setAttribute($key, $clientData[$key]); |
315
|
|
|
$attributes[$key] = $clientData[$key]; |
316
|
|
|
// @codeCoverageIgnoreStart |
317
|
|
|
} elseif ($record->canSetProperty($key)) { |
318
|
|
|
// TODO: find a way to test attribute population |
319
|
|
|
$record->{$key} = $value; |
320
|
|
|
} |
321
|
|
|
// @codeCoverageIgnoreEnd |
322
|
|
|
} |
323
|
|
|
if (empty($attributes) === false) { |
324
|
|
|
$record->setOldAttributes($attributes); |
325
|
|
|
} |
326
|
|
|
$record->afterFind(); |
327
|
|
|
} |
328
|
|
|
return $record; |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* @inheritdoc |
333
|
|
|
*/ |
334
|
|
|
public function delete(ClientModelInterface $client) |
335
|
|
|
{ |
336
|
|
|
$result = false; |
337
|
|
|
if ($client->beforeDelete()) { |
338
|
|
|
//TODO: check results to return correct information |
339
|
|
|
$this->db->createCommand() |
340
|
|
|
->delete($this->clientsTable, 'id = :id', [':id' => $client->getKey()]) |
341
|
|
|
->execute(); |
342
|
|
|
$client->setIsNewRecord(true); |
343
|
|
|
$client->afterDelete(); |
344
|
|
|
$result = true; |
345
|
|
|
} |
346
|
|
|
return $result; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* @inheritdoc |
351
|
|
|
*/ |
352
|
|
|
public function hasUser(ClientModelInterface $client, $userId) |
353
|
|
|
{ |
354
|
|
|
$entity = (new Query()) |
355
|
|
|
->select('*') |
356
|
|
|
->from($this->clientUserTable) |
357
|
|
|
->where('clientId = :clientId', [':clientId' => $client->getKey()]) |
358
|
|
|
->andWhere('userId = :userId', [':userId' => $userId]) |
359
|
|
|
->one($this->db); |
360
|
|
|
return ($entity !== false); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* @inheritdoc |
365
|
|
|
*/ |
366
|
|
|
public function addUser(ClientModelInterface $client, $userId) |
367
|
|
|
{ |
368
|
|
|
$params = [ |
369
|
|
|
'clientId' => $client->getKey(), |
370
|
|
|
'userId' => $userId |
371
|
|
|
]; |
372
|
|
|
$this->db->createCommand() |
373
|
|
|
->insert($this->clientUserTable, $params) |
374
|
|
|
->execute(); |
375
|
|
|
return true; |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* @inheritdoc |
380
|
|
|
*/ |
381
|
|
|
public function removeUser(ClientModelInterface $client, $userId) |
382
|
|
|
{ |
383
|
|
|
$this->db->createCommand() |
384
|
|
|
->delete($this->clientUserTable, |
385
|
|
|
'clientId = :clientId AND userId = :userId', |
386
|
|
|
[':clientId' => $client->getKey(), ':userId' => $userId]) |
387
|
|
|
->execute(); |
388
|
|
|
return true; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* @inheritdoc |
393
|
|
|
*/ |
394
|
|
|
public function findAllByUserId($userId) |
395
|
|
|
{ |
396
|
|
|
$clientsList = (new Query()) |
397
|
|
|
->select('*') |
398
|
|
|
->from($this->clientUserTable) |
399
|
|
|
->where('userId = :userId', [':userId' => $userId]) |
400
|
|
|
->all($this->db); |
401
|
|
|
$clients = []; |
402
|
|
|
foreach ($clientsList as $client) { |
403
|
|
|
$result = $this->findOne($client['clientId']); |
404
|
|
|
if ($result instanceof ClientModelInterface) { |
405
|
|
|
$clients[] = $result; |
406
|
|
|
} |
407
|
|
|
} |
408
|
|
|
return $clients; |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* @inheritdoc |
413
|
|
|
*/ |
414
|
|
|
public function findAll() |
415
|
|
|
{ |
416
|
|
|
$clientsList = (new Query()) |
417
|
|
|
->select('*') |
418
|
|
|
->from($this->clientsTable) |
419
|
|
|
->all($this->db); |
420
|
|
|
$clients = []; |
421
|
|
|
foreach ($clientsList as $client) { |
422
|
|
|
$result = $this->findOne($client['id']); |
423
|
|
|
if ($result instanceof ClientModelInterface) { |
424
|
|
|
$clients[] = $result; |
425
|
|
|
} |
426
|
|
|
} |
427
|
|
|
return $clients; |
428
|
|
|
} |
429
|
|
|
} |
430
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.