統計
| ブランチ: | リビジョン:

pictcode / lib / Cake / Console / Command / AclShell.php @ 0b1b8047

履歴 | 表示 | アノテート | ダウンロード (19.064 KB)

1 635eef61 spyder1211
<?php
2
/**
3
 * Acl Shell provides Acl access in the CLI environment
4
 *
5
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
6
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
7
 *
8
 * Licensed under The MIT License
9
 * For full copyright and license information, please see the LICENSE.txt
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
13
 * @link          http://cakephp.org CakePHP(tm) Project
14
 * @since         CakePHP(tm) v 1.2.0.5012
15
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
16
 */
17
18
App::uses('AppShell', 'Console/Command');
19
App::uses('Controller', 'Controller');
20
App::uses('ComponentCollection', 'Controller');
21
App::uses('AclComponent', 'Controller/Component');
22
App::uses('DbAcl', 'Model');
23
App::uses('Hash', 'Utility');
24
25
/**
26
 * Shell for ACL management. This console is known to have issues with zend.ze1_compatibility_mode
27
 * being enabled. Be sure to turn it off when using this shell.
28
 *
29
 * @package       Cake.Console.Command
30
 */
31
class AclShell extends AppShell {
32
33
/**
34
 * Contains instance of AclComponent
35
 *
36
 * @var AclComponent
37
 */
38
        public $Acl;
39
40
/**
41
 * Contains arguments parsed from the command line.
42
 *
43
 * @var array
44
 */
45
        public $args;
46
47
/**
48
 * Contains database source to use
49
 *
50
 * @var string
51
 */
52
        public $connection = 'default';
53
54
/**
55
 * Contains tasks to load and instantiate
56
 *
57
 * @var array
58
 */
59
        public $tasks = array('DbConfig');
60
61
/**
62
 * Override startup of the Shell
63
 *
64
 * @return void
65
 */
66
        public function startup() {
67
                parent::startup();
68
                if (isset($this->params['connection'])) {
69
                        $this->connection = $this->params['connection'];
70
                }
71
72
                $class = Configure::read('Acl.classname');
73
                list($plugin, $class) = pluginSplit($class, true);
74
                App::uses($class, $plugin . 'Controller/Component/Acl');
75
                if (!in_array($class, array('DbAcl', 'DB_ACL')) && !is_subclass_of($class, 'DbAcl')) {
76
                        $out = "--------------------------------------------------\n";
77
                        $out .= __d('cake_console', 'Error: Your current CakePHP configuration is set to an ACL implementation other than DB.') . "\n";
78
                        $out .= __d('cake_console', 'Please change your core config to reflect your decision to use DbAcl before attempting to use this script') . "\n";
79
                        $out .= "--------------------------------------------------\n";
80
                        $out .= __d('cake_console', 'Current ACL Classname: %s', $class) . "\n";
81
                        $out .= "--------------------------------------------------\n";
82
                        $this->err($out);
83
                        return $this->_stop();
84
                }
85
86
                if ($this->command) {
87
                        if (!config('database')) {
88
                                $this->out(__d('cake_console', 'Your database configuration was not found. Take a moment to create one.'));
89
                                $this->args = null;
90
                                return $this->DbConfig->execute();
91
                        }
92
                        require_once APP . 'Config' . DS . 'database.php';
93
94
                        if (!in_array($this->command, array('initdb'))) {
95
                                $collection = new ComponentCollection();
96
                                $this->Acl = new AclComponent($collection);
97
                                $controller = new Controller();
98
                                $this->Acl->startup($controller);
99
                        }
100
                }
101
        }
102
103
/**
104
 * Override main() for help message hook
105
 *
106
 * @return void
107
 */
108
        public function main() {
109
                $this->out($this->OptionParser->help());
110
        }
111
112
/**
113
 * Creates an ARO/ACO node
114
 *
115
 * @return void
116
 */
117
        public function create() {
118
                extract($this->_dataVars());
119
120
                $class = ucfirst($this->args[0]);
121
                $parent = $this->parseIdentifier($this->args[1]);
122
123
                if (!empty($parent) && $parent !== '/' && $parent !== 'root') {
124
                        $parent = $this->_getNodeId($class, $parent);
125
                } else {
126
                        $parent = null;
127
                }
128
129
                $data = $this->parseIdentifier($this->args[2]);
130
                if (is_string($data) && $data !== '/') {
131
                        $data = array('alias' => $data);
132
                } elseif (is_string($data)) {
133
                        $this->error(__d('cake_console', '/ can not be used as an alias!') . __d('cake_console', "        / is the root, please supply a sub alias"));
134
                }
135
136
                $data['parent_id'] = $parent;
137
                $this->Acl->{$class}->create();
138
                if ($this->Acl->{$class}->save($data)) {
139
                        $this->out(__d('cake_console', "<success>New %s</success> '%s' created.", $class, $this->args[2]), 2);
140
                } else {
141
                        $this->err(__d('cake_console', "There was a problem creating a new %s '%s'.", $class, $this->args[2]));
142
                }
143
        }
144
145
/**
146
 * Delete an ARO/ACO node. Note there may be (as a result of poor configuration)
147
 * multiple records with the same logical identifier. All are deleted.
148
 *
149
 * @return void
150
 */
151
        public function delete() {
152
                extract($this->_dataVars());
153
154
                $identifier = $this->parseIdentifier($this->args[1]);
155
                if (is_string($identifier)) {
156
                        $identifier = array('alias' => $identifier);
157
                }
158
159
                if ($this->Acl->{$class}->find('all', array('conditions' => $identifier))) {
160
                        if (!$this->Acl->{$class}->deleteAll($identifier)) {
161
                                $this->error(__d('cake_console', 'Node Not Deleted. ') . __d('cake_console', 'There was an error deleting the %s.', $class) . "\n");
162
                        }
163
                        $this->out(__d('cake_console', '<success>%s deleted.</success>', $class), 2);
164
                } else {
165
                        $this->error(__d('cake_console', 'Node Not Deleted. ') . __d('cake_console', 'There was an error deleting the %s. Node does not exist.', $class) . "\n");
166
                }
167
        }
168
169
/**
170
 * Set parent for an ARO/ACO node.
171
 *
172
 * @return void
173
 */
174
        public function setParent() {
175
                extract($this->_dataVars());
176
                $target = $this->parseIdentifier($this->args[1]);
177
                $parent = $this->parseIdentifier($this->args[2]);
178
179
                $data = array(
180
                        $class => array(
181
                                'id' => $this->_getNodeId($class, $target),
182
                                'parent_id' => $this->_getNodeId($class, $parent)
183
                        )
184
                );
185
                $this->Acl->{$class}->create();
186
                if (!$this->Acl->{$class}->save($data)) {
187
                        $this->out(__d('cake_console', 'Error in setting new parent. Please make sure the parent node exists, and is not a descendant of the node specified.'));
188
                } else {
189
                        $this->out(__d('cake_console', 'Node parent set to %s', $this->args[2]) . "\n");
190
                }
191
        }
192
193
/**
194
 * Get path to specified ARO/ACO node.
195
 *
196
 * @return void
197
 */
198
        public function getPath() {
199
                extract($this->_dataVars());
200
                $identifier = $this->parseIdentifier($this->args[1]);
201
202
                $id = $this->_getNodeId($class, $identifier);
203
                $nodes = $this->Acl->{$class}->getPath($id);
204
205
                if (empty($nodes)) {
206
                        $this->error(
207
                                __d('cake_console', "Supplied Node '%s' not found", $this->args[1]),
208
                                __d('cake_console', 'No tree returned.')
209
                        );
210
                }
211
                $this->out(__d('cake_console', 'Path:'));
212
                $this->hr();
213
                for ($i = 0, $len = count($nodes); $i < $len; $i++) {
214
                        $this->_outputNode($class, $nodes[$i], $i);
215
                }
216
        }
217
218
/**
219
 * Outputs a single node, Either using the alias or Model.key
220
 *
221
 * @param string $class Class name that is being used.
222
 * @param array $node Array of node information.
223
 * @param int $indent indent level.
224
 * @return void
225
 */
226
        protected function _outputNode($class, $node, $indent) {
227
                $indent = str_repeat('  ', $indent);
228
                $data = $node[$class];
229
                if ($data['alias']) {
230
                        $this->out($indent . "[" . $data['id'] . "] " . $data['alias']);
231
                } else {
232
                        $this->out($indent . "[" . $data['id'] . "] " . $data['model'] . '.' . $data['foreign_key']);
233
                }
234
        }
235
236
/**
237
 * Check permission for a given ARO to a given ACO.
238
 *
239
 * @return void
240
 */
241
        public function check() {
242
                extract($this->_getParams());
243
244
                if ($this->Acl->check($aro, $aco, $action)) {
245
                        $this->out(__d('cake_console', '%s is <success>allowed</success>.', $aroName));
246
                } else {
247
                        $this->out(__d('cake_console', '%s is <error>not allowed</error>.', $aroName));
248
                }
249
        }
250
251
/**
252
 * Grant permission for a given ARO to a given ACO.
253
 *
254
 * @return void
255
 */
256
        public function grant() {
257
                extract($this->_getParams());
258
259
                if ($this->Acl->allow($aro, $aco, $action)) {
260
                        $this->out(__d('cake_console', 'Permission <success>granted</success>.'));
261
                } else {
262
                        $this->out(__d('cake_console', 'Permission was <error>not granted</error>.'));
263
                }
264
        }
265
266
/**
267
 * Deny access for an ARO to an ACO.
268
 *
269
 * @return void
270
 */
271
        public function deny() {
272
                extract($this->_getParams());
273
274
                if ($this->Acl->deny($aro, $aco, $action)) {
275
                        $this->out(__d('cake_console', 'Permission denied.'));
276
                } else {
277
                        $this->out(__d('cake_console', 'Permission was not denied.'));
278
                }
279
        }
280
281
/**
282
 * Set an ARO to inherit permission to an ACO.
283
 *
284
 * @return void
285
 */
286
        public function inherit() {
287
                extract($this->_getParams());
288
289
                if ($this->Acl->inherit($aro, $aco, $action)) {
290
                        $this->out(__d('cake_console', 'Permission inherited.'));
291
                } else {
292
                        $this->out(__d('cake_console', 'Permission was not inherited.'));
293
                }
294
        }
295
296
/**
297
 * Show a specific ARO/ACO node.
298
 *
299
 * @return void
300
 */
301
        public function view() {
302
                extract($this->_dataVars());
303
304
                if (isset($this->args[1])) {
305
                        $identity = $this->parseIdentifier($this->args[1]);
306
307
                        $topNode = $this->Acl->{$class}->find('first', array(
308
                                'conditions' => array($class . '.id' => $this->_getNodeId($class, $identity))
309
                        ));
310
311
                        $nodes = $this->Acl->{$class}->find('all', array(
312
                                'conditions' => array(
313
                                        $class . '.lft >=' => $topNode[$class]['lft'],
314
                                        $class . '.lft <=' => $topNode[$class]['rght']
315
                                ),
316
                                'order' => $class . '.lft ASC'
317
                        ));
318
                } else {
319
                        $nodes = $this->Acl->{$class}->find('all', array('order' => $class . '.lft ASC'));
320
                }
321
322
                if (empty($nodes)) {
323
                        if (isset($this->args[1])) {
324
                                $this->error(__d('cake_console', '%s not found', $this->args[1]), __d('cake_console', 'No tree returned.'));
325
                        } elseif (isset($this->args[0])) {
326
                                $this->error(__d('cake_console', '%s not found', $this->args[0]), __d('cake_console', 'No tree returned.'));
327
                        }
328
                }
329
                $this->out($class . ' tree:');
330
                $this->hr();
331
332
                $stack = array();
333
                $last = null;
334
335
                foreach ($nodes as $n) {
336
                        $stack[] = $n;
337
                        if (!empty($last)) {
338
                                $end = end($stack);
339
                                if ($end[$class]['rght'] > $last) {
340
                                        foreach ($stack as $k => $v) {
341
                                                $end = end($stack);
342
                                                if ($v[$class]['rght'] < $end[$class]['rght']) {
343
                                                        unset($stack[$k]);
344
                                                }
345
                                        }
346
                                }
347
                        }
348
                        $last = $n[$class]['rght'];
349
                        $count = count($stack);
350
351
                        $this->_outputNode($class, $n, $count);
352
                }
353
                $this->hr();
354
        }
355
356
/**
357
 * Initialize ACL database.
358
 *
359
 * @return mixed
360
 */
361
        public function initdb() {
362
                return $this->dispatchShell('schema create DbAcl');
363
        }
364
365
/**
366
 * Gets the option parser instance and configures it.
367
 *
368
 * @return ConsoleOptionParser
369
 */
370
        public function getOptionParser() {
371
                $parser = parent::getOptionParser();
372
373
                $type = array(
374
                        'choices' => array('aro', 'aco'),
375
                        'required' => true,
376
                        'help' => __d('cake_console', 'Type of node to create.')
377
                );
378
379
                $parser->description(
380
                        __d('cake_console', 'A console tool for managing the DbAcl')
381
                )->addSubcommand('create', array(
382
                        'help' => __d('cake_console', 'Create a new ACL node'),
383
                        'parser' => array(
384
                                'description' => __d('cake_console', 'Creates a new ACL object <node> under the parent'),
385
                                'epilog' => __d('cake_console', 'You can use `root` as the parent when creating nodes to create top level nodes.'),
386
                                'arguments' => array(
387
                                        'type' => $type,
388
                                        'parent' => array(
389
                                                'help' => __d('cake_console', 'The node selector for the parent.'),
390
                                                'required' => true
391
                                        ),
392
                                        'alias' => array(
393
                                                'help' => __d('cake_console', 'The alias to use for the newly created node.'),
394
                                                'required' => true
395
                                        )
396
                                )
397
                        )
398
                ))->addSubcommand('delete', array(
399
                        'help' => __d('cake_console', 'Deletes the ACL object with the given <node> reference'),
400
                        'parser' => array(
401
                                'description' => __d('cake_console', 'Delete an ACL node.'),
402
                                'arguments' => array(
403
                                        'type' => $type,
404
                                        'node' => array(
405
                                                'help' => __d('cake_console', 'The node identifier to delete.'),
406
                                                'required' => true,
407
                                        )
408
                                )
409
                        )
410
                ))->addSubcommand('setparent', array(
411
                        'help' => __d('cake_console', 'Moves the ACL node under a new parent.'),
412
                        'parser' => array(
413
                                'description' => __d('cake_console', 'Moves the ACL object specified by <node> beneath <parent>'),
414
                                'arguments' => array(
415
                                        'type' => $type,
416
                                        'node' => array(
417
                                                'help' => __d('cake_console', 'The node to move'),
418
                                                'required' => true,
419
                                        ),
420
                                        'parent' => array(
421
                                                'help' => __d('cake_console', 'The new parent for <node>.'),
422
                                                'required' => true
423
                                        )
424
                                )
425
                        )
426
                ))->addSubcommand('getpath', array(
427
                        'help' => __d('cake_console', 'Print out the path to an ACL node.'),
428
                        'parser' => array(
429
                                'description' => array(
430
                                        __d('cake_console', "Returns the path to the ACL object specified by <node>."),
431
                                        __d('cake_console', "This command is useful in determining the inheritance of permissions for a certain object in the tree.")
432
                                ),
433
                                'arguments' => array(
434
                                        'type' => $type,
435
                                        'node' => array(
436
                                                'help' => __d('cake_console', 'The node to get the path of'),
437
                                                'required' => true,
438
                                        )
439
                                )
440
                        )
441
                ))->addSubcommand('check', array(
442
                        'help' => __d('cake_console', 'Check the permissions between an ACO and ARO.'),
443
                        'parser' => array(
444
                                'description' => array(
445
                                        __d('cake_console', 'Use this command to check ACL permissions.')
446
                                ),
447
                                'arguments' => array(
448
                                        'aro' => array('help' => __d('cake_console', 'ARO to check.'), 'required' => true),
449
                                        'aco' => array('help' => __d('cake_console', 'ACO to check.'), 'required' => true),
450
                                        'action' => array('help' => __d('cake_console', 'Action to check'), 'default' => 'all')
451
                                )
452
                        )
453
                ))->addSubcommand('grant', array(
454
                        'help' => __d('cake_console', 'Grant an ARO permissions to an ACO.'),
455
                        'parser' => array(
456
                                'description' => array(
457
                                        __d('cake_console', 'Use this command to grant ACL permissions. Once executed, the ARO specified (and its children, if any) will have ALLOW access to the specified ACO action (and the ACO\'s children, if any).')
458
                                ),
459
                                'arguments' => array(
460
                                        'aro' => array('help' => __d('cake_console', 'ARO to grant permission to.'), 'required' => true),
461
                                        'aco' => array('help' => __d('cake_console', 'ACO to grant access to.'), 'required' => true),
462
                                        'action' => array('help' => __d('cake_console', 'Action to grant'), 'default' => 'all')
463
                                )
464
                        )
465
                ))->addSubcommand('deny', array(
466
                        'help' => __d('cake_console', 'Deny an ARO permissions to an ACO.'),
467
                        'parser' => array(
468
                                'description' => array(
469
                                        __d('cake_console', 'Use this command to deny ACL permissions. Once executed, the ARO specified (and its children, if any) will have DENY access to the specified ACO action (and the ACO\'s children, if any).')
470
                                ),
471
                                'arguments' => array(
472
                                        'aro' => array('help' => __d('cake_console', 'ARO to deny.'), 'required' => true),
473
                                        'aco' => array('help' => __d('cake_console', 'ACO to deny.'), 'required' => true),
474
                                        'action' => array('help' => __d('cake_console', 'Action to deny'), 'default' => 'all')
475
                                )
476
                        )
477
                ))->addSubcommand('inherit', array(
478
                        'help' => __d('cake_console', 'Inherit an ARO\'s parent permissions.'),
479
                        'parser' => array(
480
                                'description' => array(
481
                                        __d('cake_console', "Use this command to force a child ARO object to inherit its permissions settings from its parent.")
482
                                ),
483
                                'arguments' => array(
484
                                        'aro' => array('help' => __d('cake_console', 'ARO to have permissions inherit.'), 'required' => true),
485
                                        'aco' => array('help' => __d('cake_console', 'ACO to inherit permissions on.'), 'required' => true),
486
                                        'action' => array('help' => __d('cake_console', 'Action to inherit'), 'default' => 'all')
487
                                )
488
                        )
489
                ))->addSubcommand('view', array(
490
                        'help' => __d('cake_console', 'View a tree or a single node\'s subtree.'),
491
                        'parser' => array(
492
                                'description' => array(
493
                                        __d('cake_console', "The view command will return the ARO or ACO tree."),
494
                                        __d('cake_console', "The optional node parameter allows you to return"),
495
                                        __d('cake_console', "only a portion of the requested tree.")
496
                                ),
497
                                'arguments' => array(
498
                                        'type' => $type,
499
                                        'node' => array('help' => __d('cake_console', 'The optional node to view the subtree of.'))
500
                                )
501
                        )
502
                ))->addSubcommand('initdb', array(
503
                        'help' => __d('cake_console', 'Initialize the DbAcl tables. Uses this command : cake schema create DbAcl')
504
                ))->epilog(array(
505
                        'Node and parent arguments can be in one of the following formats:',
506
                        '',
507
                        ' - <model>.<id> - The node will be bound to a specific record of the given model.',
508
                        '',
509
                        ' - <alias> - The node will be given a string alias (or path, in the case of <parent>)',
510
                        "   i.e. 'John'. When used with <parent>, this takes the form of an alias path,",
511
                        "   i.e. <group>/<subgroup>/<parent>.",
512
                        '',
513
                        "To add a node at the root level, enter 'root' or '/' as the <parent> parameter."
514
                ));
515
516
                return $parser;
517
        }
518
519
/**
520
 * Checks that given node exists
521
 *
522
 * @return bool Success
523
 */
524
        public function nodeExists() {
525
                if (!isset($this->args[0]) || !isset($this->args[1])) {
526
                        return false;
527
                }
528
                $dataVars = $this->_dataVars($this->args[0]);
529
                extract($dataVars);
530
                $key = is_numeric($this->args[1]) ? $dataVars['secondary_id'] : 'alias';
531
                $conditions = array($class . '.' . $key => $this->args[1]);
532
                $possibility = $this->Acl->{$class}->find('all', compact('conditions'));
533
                if (empty($possibility)) {
534
                        $this->error(__d('cake_console', '%s not found', $this->args[1]), __d('cake_console', 'No tree returned.'));
535
                }
536
                return $possibility;
537
        }
538
539
/**
540
 * Parse an identifier into Model.foreignKey or an alias.
541
 * Takes an identifier determines its type and returns the result as used by other methods.
542
 *
543
 * @param string $identifier Identifier to parse
544
 * @return mixed a string for aliases, and an array for model.foreignKey
545
 */
546
        public function parseIdentifier($identifier) {
547
                if (preg_match('/^([\w]+)\.(.*)$/', $identifier, $matches)) {
548
                        return array(
549
                                'model' => $matches[1],
550
                                'foreign_key' => $matches[2],
551
                        );
552
                }
553
                return $identifier;
554
        }
555
556
/**
557
 * Get the node for a given identifier. $identifier can either be a string alias
558
 * or an array of properties to use in AcoNode::node()
559
 *
560
 * @param string $class Class type you want (Aro/Aco)
561
 * @param string|array|null $identifier A mixed identifier for finding the node, otherwise null.
562
 * @return int Integer of NodeId. Will trigger an error if nothing is found.
563
 */
564
        protected function _getNodeId($class, $identifier) {
565
                $node = $this->Acl->{$class}->node($identifier);
566
                if (empty($node)) {
567
                        if (is_array($identifier)) {
568
                                $identifier = var_export($identifier, true);
569
                        }
570
                        $this->error(__d('cake_console', 'Could not find node using reference "%s"', $identifier));
571
                        return null;
572
                }
573
                return Hash::get($node, "0.{$class}.id");
574
        }
575
576
/**
577
 * get params for standard Acl methods
578
 *
579
 * @return array aro, aco, action
580
 */
581
        protected function _getParams() {
582
                $aro = is_numeric($this->args[0]) ? (int)$this->args[0] : $this->args[0];
583
                $aco = is_numeric($this->args[1]) ? (int)$this->args[1] : $this->args[1];
584
                $aroName = $aro;
585
                $acoName = $aco;
586
587
                if (is_string($aro)) {
588
                        $aro = $this->parseIdentifier($aro);
589
                }
590
                if (is_string($aco)) {
591
                        $aco = $this->parseIdentifier($aco);
592
                }
593
                $action = '*';
594
                if (isset($this->args[2]) && !in_array($this->args[2], array('', 'all'))) {
595
                        $action = $this->args[2];
596
                }
597
                return compact('aro', 'aco', 'action', 'aroName', 'acoName');
598
        }
599
600
/**
601
 * Build data parameters based on node type
602
 *
603
 * @param string $type Node type  (ARO/ACO)
604
 * @return array Variables
605
 */
606
        protected function _dataVars($type = null) {
607
                if (!$type) {
608
                        $type = $this->args[0];
609
                }
610
                $vars = array();
611
                $class = ucwords($type);
612
                $vars['secondary_id'] = (strtolower($class) === 'aro') ? 'foreign_key' : 'object_id';
613
                $vars['data_name'] = $type;
614
                $vars['table_name'] = $type . 's';
615
                $vars['class'] = $class;
616
                return $vars;
617
        }
618
619
}