1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* BEdita, API-first content management framework |
4
|
|
|
* Copyright 2017 ChannelWeb Srl, Chialab Srl |
5
|
|
|
* |
6
|
|
|
* This file is part of BEdita: you can redistribute it and/or modify |
7
|
|
|
* it under the terms of the GNU Lesser General Public License as published |
8
|
|
|
* by the Free Software Foundation, either version 3 of the License, or |
9
|
|
|
* (at your option) any later version. |
10
|
|
|
* |
11
|
|
|
* See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details. |
12
|
|
|
*/ |
13
|
|
|
namespace BEdita\Core\Shell; |
14
|
|
|
|
15
|
|
|
use BEdita\Core\Model\Action\DeleteEntityAction; |
16
|
|
|
use BEdita\Core\Model\Action\ListEntitiesAction; |
17
|
|
|
use Cake\Console\Shell; |
18
|
|
|
use Cake\ORM\Entity; |
19
|
|
|
use Cake\ORM\TableRegistry; |
20
|
|
|
use Cake\Utility\Inflector; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Resource shell commands: list, create, remove, enable and disable common entities |
24
|
|
|
* |
25
|
|
|
* @since 4.0.0 |
26
|
|
|
*/ |
27
|
|
|
class ResourcesShell extends Shell |
28
|
|
|
{ |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Accepted resource types |
32
|
|
|
* |
33
|
|
|
* @var array |
34
|
|
|
*/ |
35
|
|
|
protected $acceptedTypes = ['applications', 'roles', 'endpoints', 'endpoint_permissions']; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Editable resource fields |
39
|
|
|
* |
40
|
|
|
* @var array |
41
|
|
|
*/ |
42
|
|
|
protected $editableFields = ['api_key', 'description', 'enabled', 'name', 'unchangeable']; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Resource model table |
46
|
|
|
* |
47
|
|
|
* @var \Cake\ORM\Table |
48
|
|
|
*/ |
49
|
|
|
protected $modelTable = null; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* {@inheritDoc} |
53
|
|
|
* |
54
|
|
|
* @codeCoverageIgnore |
55
|
|
|
*/ |
56
|
|
|
public function getOptionParser() |
57
|
|
|
{ |
58
|
|
|
$parser = parent::getOptionParser(); |
59
|
|
|
|
60
|
|
|
$options = [ |
61
|
|
|
'type' => [ |
62
|
|
|
'help' => 'Entity type (applications, roles, endpoints)', |
63
|
|
|
'short' => 't', |
64
|
|
|
'required' => true, |
65
|
|
|
'default' => null, |
66
|
|
|
'choices' => $this->acceptedTypes, |
67
|
|
|
], |
68
|
|
|
]; |
69
|
|
|
|
70
|
|
|
$arguments = [ |
71
|
|
|
'name|id' => [ |
72
|
|
|
'help' => 'Resource\'s name or id', |
73
|
|
|
'required' => true |
74
|
|
|
], |
75
|
|
|
]; |
76
|
|
|
|
77
|
|
|
$parser->addSubcommand('add', [ |
78
|
|
|
'help' => 'create a new entity', |
79
|
|
|
'parser' => [ |
80
|
|
|
'description' => [ |
81
|
|
|
'Create a new resource.' |
82
|
|
|
], |
83
|
|
|
'options' => $options, |
84
|
|
|
] |
85
|
|
|
]); |
86
|
|
|
$parser->addSubcommand('ls', [ |
87
|
|
|
'help' => 'list entities', |
88
|
|
|
'parser' => [ |
89
|
|
|
'description' => [ |
90
|
|
|
'List entities.', |
91
|
|
|
'Option --filter (optional) provides listing filtered by comma separated key=value pairs.' |
92
|
|
|
], |
93
|
|
|
'options' => $options + [ |
94
|
|
|
'filter' => [ |
95
|
|
|
'help' => 'List entities filtered by comma separated key=value pairs', |
96
|
|
|
'required' => false |
97
|
|
|
], |
98
|
|
|
], |
99
|
|
|
] |
100
|
|
|
]); |
101
|
|
|
$parser->addSubcommand('rm', [ |
102
|
|
|
'help' => 'remove an entity', |
103
|
|
|
'parser' => [ |
104
|
|
|
'description' => [ |
105
|
|
|
'Remove an entity.', |
106
|
|
|
'First argument (required) indicates entity\'s id|name.' |
107
|
|
|
], |
108
|
|
|
'arguments' => $arguments, |
109
|
|
|
'options' => $options, |
110
|
|
|
] |
111
|
|
|
]); |
112
|
|
|
$parser->addSubcommand('edit', [ |
113
|
|
|
'help' => 'modify an entity field', |
114
|
|
|
'parser' => [ |
115
|
|
|
'description' => [ |
116
|
|
|
'Modify a field on a single resource.', |
117
|
|
|
'Required entity\'s id|name and field' |
118
|
|
|
], |
119
|
|
|
'arguments' => $arguments, |
120
|
|
|
'options' => $options + [ |
121
|
|
|
'field' => [ |
122
|
|
|
'help' => 'Field name', |
123
|
|
|
'short' => 'f', |
124
|
|
|
'required' => true, |
125
|
|
|
'choices' => $this->editableFields, |
126
|
|
|
], |
127
|
|
|
], |
128
|
|
|
] |
129
|
|
|
]); |
130
|
|
|
|
131
|
|
|
return $parser; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Init model table using --type|-t option |
136
|
|
|
* |
137
|
|
|
* @return void |
138
|
|
|
*/ |
139
|
|
|
protected function initModel() |
140
|
|
|
{ |
141
|
|
|
$modelName = Inflector::camelize($this->param('type')); |
|
|
|
|
142
|
|
|
$this->modelTable = TableRegistry::get($modelName); |
|
|
|
|
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Create a new resource |
147
|
|
|
* |
148
|
|
|
* @return \Cake\ORM\Entity $entity Entity created |
149
|
|
|
*/ |
150
|
|
|
public function add() |
151
|
|
|
{ |
152
|
|
|
$this->initModel(); |
153
|
|
|
$entity = $this->modelTable->newEntity(); |
154
|
|
|
if ($this->param('type') === 'endpoint_permissions') { |
155
|
|
|
$this->setupEndpointPermissionEntity($entity); |
|
|
|
|
156
|
|
|
} else { |
157
|
|
|
$this->setupDefaultEntity($entity); |
|
|
|
|
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
$this->modelTable->save($entity); |
161
|
|
|
$this->out('Resource with id ' . $entity->id . ' created'); |
|
|
|
|
162
|
|
|
|
163
|
|
|
return $entity; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Setup entity for endpoint_permissions |
168
|
|
|
* |
169
|
|
|
* @param \Cake\ORM\Entity $entity Entity to add |
170
|
|
|
* @return void |
171
|
|
|
*/ |
172
|
|
|
protected function setupEndpointPermissionEntity($entity) |
173
|
|
|
{ |
174
|
|
|
$fieldsTables = [ |
175
|
|
|
'application_id' => 'Applications', |
176
|
|
|
'endpoint_id' => 'Endpoints', |
177
|
|
|
'role_id' => 'Roles', |
178
|
|
|
]; |
179
|
|
|
foreach ($fieldsTables as $field => $table) { |
180
|
|
|
$id = $this->in($table . ' id or name'); |
181
|
|
|
if ($id && !is_numeric($id)) { |
182
|
|
|
$id = TableRegistry::get($table)->find()->where(['name' => $id])->firstOrFail()->id; |
183
|
|
|
} |
184
|
|
|
$entity->$field = $id; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
$perms = ['true', 'false', 'block', 'mine']; |
188
|
|
|
foreach (['read', 'write'] as $field) { |
189
|
|
|
$perm = $this->in("'$field' permission", $perms); |
190
|
|
|
$entity->$field = $perm; |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Setup default entity for applications, roles, endpoints |
196
|
|
|
* |
197
|
|
|
* @param \Cake\ORM\Entity $entity Entity to add |
198
|
|
|
* @return void |
199
|
|
|
*/ |
200
|
|
|
protected function setupDefaultEntity($entity) |
201
|
|
|
{ |
202
|
|
|
$name = $this->in('Resource name'); |
203
|
|
|
if (empty($name)) { |
204
|
|
|
$this->abort('Resource name cannot be empty'); |
205
|
|
|
} |
206
|
|
|
$entity->name = $name; |
207
|
|
|
$description = $this->in('Resource description (optional)'); |
208
|
|
|
$entity->description = $description; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Modify a resource field |
213
|
|
|
* |
214
|
|
|
* @param mixed $id Resource sid or name |
215
|
|
|
* @return void |
216
|
|
|
*/ |
217
|
|
|
public function edit($id) |
218
|
|
|
{ |
219
|
|
|
$this->initModel(); |
220
|
|
|
$entity = $this->getEntity($id); |
221
|
|
|
$field = $this->param('field'); |
222
|
|
|
if ($field === 'api_key') { |
223
|
|
|
$entity->api_key = $this->modelTable->generateApiKey(); |
224
|
|
|
} else { |
225
|
|
|
$value = $this->in(sprintf('New value for "%s" [current is "%s"]', $field, $entity->get($field))); |
|
|
|
|
226
|
|
|
$entity->set($field, $value); |
|
|
|
|
227
|
|
|
} |
228
|
|
|
$this->modelTable->save($entity); |
229
|
|
|
$this->out('Resource with id ' . $entity->id . ' modified'); |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* List entities |
234
|
|
|
* |
235
|
|
|
* @return array applications list |
236
|
|
|
*/ |
237
|
|
|
public function ls() |
238
|
|
|
{ |
239
|
|
|
$this->initModel(); |
240
|
|
|
$action = new ListEntitiesAction(['table' => $this->modelTable]); |
241
|
|
|
$query = $action(['filter' => $this->param('filter')]); |
242
|
|
|
$results = $query->toArray(); |
243
|
|
|
$this->out($results ?: 'empty set'); |
244
|
|
|
|
245
|
|
|
return $results; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Remove entity by name or id |
250
|
|
|
* |
251
|
|
|
* @param mixed $id Resource id or name |
252
|
|
|
* @return bool True on success, false on blocked execution |
253
|
|
|
*/ |
254
|
|
|
public function rm($id) |
255
|
|
|
{ |
256
|
|
|
$this->initModel(); |
257
|
|
|
$res = $this->in(sprintf('You are REMOVING "%s" with name or id "%s" - are you sure?', $this->param('type'), $id), ['y', 'n'], 'n'); |
258
|
|
|
if ($res != 'y') { |
259
|
|
|
$this->info('Remove not executed'); |
260
|
|
|
|
261
|
|
|
return false; |
262
|
|
|
} |
263
|
|
|
$entity = $this->getEntity($id); |
264
|
|
|
$action = new DeleteEntityAction(['table' => $this->modelTable]); |
265
|
|
|
$action(compact('entity')); |
266
|
|
|
|
267
|
|
|
$this->out('Record ' . $id . ' deleted'); |
268
|
|
|
|
269
|
|
|
return true; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Return entity by $id name|id |
274
|
|
|
* |
275
|
|
|
* @param mixed $id entity name|id |
276
|
|
|
* @return \Cake\ORM\Entity entity |
277
|
|
|
*/ |
278
|
|
|
protected function getEntity($id) |
279
|
|
|
{ |
280
|
|
|
if (!is_numeric($id)) { |
281
|
|
|
return $this->modelTable |
|
|
|
|
282
|
|
|
->find() |
283
|
|
|
->where(['name' => $id]) |
284
|
|
|
->firstOrFail(); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
return $this->modelTable->get($id); |
288
|
|
|
} |
289
|
|
|
} |
290
|
|
|
|
This check looks at variables that 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.