Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
13532 anikendra 1
<?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.
147
 *
148
 * @return void
149
 */
150
	public function delete() {
151
		extract($this->_dataVars());
152
 
153
		$identifier = $this->parseIdentifier($this->args[1]);
154
		$nodeId = $this->_getNodeId($class, $identifier);
155
 
156
		if (!$this->Acl->{$class}->delete($nodeId)) {
157
			$this->error(__d('cake_console', 'Node Not Deleted') . __d('cake_console', 'There was an error deleting the %s. Check that the node exists.', $class) . "\n");
158
		}
159
		$this->out(__d('cake_console', '<success>%s deleted.</success>', $class), 2);
160
	}
161
 
162
/**
163
 * Set parent for an ARO/ACO node.
164
 *
165
 * @return void
166
 */
167
	public function setParent() {
168
		extract($this->_dataVars());
169
		$target = $this->parseIdentifier($this->args[1]);
170
		$parent = $this->parseIdentifier($this->args[2]);
171
 
172
		$data = array(
173
			$class => array(
174
				'id' => $this->_getNodeId($class, $target),
175
				'parent_id' => $this->_getNodeId($class, $parent)
176
			)
177
		);
178
		$this->Acl->{$class}->create();
179
		if (!$this->Acl->{$class}->save($data)) {
180
			$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.'));
181
		} else {
182
			$this->out(__d('cake_console', 'Node parent set to %s', $this->args[2]) . "\n");
183
		}
184
	}
185
 
186
/**
187
 * Get path to specified ARO/ACO node.
188
 *
189
 * @return void
190
 */
191
	public function getPath() {
192
		extract($this->_dataVars());
193
		$identifier = $this->parseIdentifier($this->args[1]);
194
 
195
		$id = $this->_getNodeId($class, $identifier);
196
		$nodes = $this->Acl->{$class}->getPath($id);
197
 
198
		if (empty($nodes)) {
199
			$this->error(
200
				__d('cake_console', "Supplied Node '%s' not found", $this->args[1]),
201
				__d('cake_console', 'No tree returned.')
202
			);
203
		}
204
		$this->out(__d('cake_console', 'Path:'));
205
		$this->hr();
206
		for ($i = 0, $len = count($nodes); $i < $len; $i++) {
207
			$this->_outputNode($class, $nodes[$i], $i);
208
		}
209
	}
210
 
211
/**
212
 * Outputs a single node, Either using the alias or Model.key
213
 *
214
 * @param string $class Class name that is being used.
215
 * @param array $node Array of node information.
216
 * @param integer $indent indent level.
217
 * @return void
218
 */
219
	protected function _outputNode($class, $node, $indent) {
220
		$indent = str_repeat('  ', $indent);
221
		$data = $node[$class];
222
		if ($data['alias']) {
223
			$this->out($indent . "[" . $data['id'] . "] " . $data['alias']);
224
		} else {
225
			$this->out($indent . "[" . $data['id'] . "] " . $data['model'] . '.' . $data['foreign_key']);
226
		}
227
	}
228
 
229
/**
230
 * Check permission for a given ARO to a given ACO.
231
 *
232
 * @return void
233
 */
234
	public function check() {
235
		extract($this->_getParams());
236
 
237
		if ($this->Acl->check($aro, $aco, $action)) {
238
			$this->out(__d('cake_console', '%s is <success>allowed</success>.', $aroName));
239
		} else {
240
			$this->out(__d('cake_console', '%s is <error>not allowed</error>.', $aroName));
241
		}
242
	}
243
 
244
/**
245
 * Grant permission for a given ARO to a given ACO.
246
 *
247
 * @return void
248
 */
249
	public function grant() {
250
		extract($this->_getParams());
251
 
252
		if ($this->Acl->allow($aro, $aco, $action)) {
253
			$this->out(__d('cake_console', 'Permission <success>granted</success>.'));
254
		} else {
255
			$this->out(__d('cake_console', 'Permission was <error>not granted</error>.'));
256
		}
257
	}
258
 
259
/**
260
 * Deny access for an ARO to an ACO.
261
 *
262
 * @return void
263
 */
264
	public function deny() {
265
		extract($this->_getParams());
266
 
267
		if ($this->Acl->deny($aro, $aco, $action)) {
268
			$this->out(__d('cake_console', 'Permission denied.'));
269
		} else {
270
			$this->out(__d('cake_console', 'Permission was not denied.'));
271
		}
272
	}
273
 
274
/**
275
 * Set an ARO to inherit permission to an ACO.
276
 *
277
 * @return void
278
 */
279
	public function inherit() {
280
		extract($this->_getParams());
281
 
282
		if ($this->Acl->inherit($aro, $aco, $action)) {
283
			$this->out(__d('cake_console', 'Permission inherited.'));
284
		} else {
285
			$this->out(__d('cake_console', 'Permission was not inherited.'));
286
		}
287
	}
288
 
289
/**
290
 * Show a specific ARO/ACO node.
291
 *
292
 * @return void
293
 */
294
	public function view() {
295
		extract($this->_dataVars());
296
 
297
		if (isset($this->args[1])) {
298
			$identity = $this->parseIdentifier($this->args[1]);
299
 
300
			$topNode = $this->Acl->{$class}->find('first', array(
301
				'conditions' => array($class . '.id' => $this->_getNodeId($class, $identity))
302
			));
303
 
304
			$nodes = $this->Acl->{$class}->find('all', array(
305
				'conditions' => array(
306
					$class . '.lft >=' => $topNode[$class]['lft'],
307
					$class . '.lft <=' => $topNode[$class]['rght']
308
				),
309
				'order' => $class . '.lft ASC'
310
			));
311
		} else {
312
			$nodes = $this->Acl->{$class}->find('all', array('order' => $class . '.lft ASC'));
313
		}
314
 
315
		if (empty($nodes)) {
316
			if (isset($this->args[1])) {
317
				$this->error(__d('cake_console', '%s not found', $this->args[1]), __d('cake_console', 'No tree returned.'));
318
			} elseif (isset($this->args[0])) {
319
				$this->error(__d('cake_console', '%s not found', $this->args[0]), __d('cake_console', 'No tree returned.'));
320
			}
321
		}
322
		$this->out($class . ' tree:');
323
		$this->hr();
324
 
325
		$stack = array();
326
		$last = null;
327
 
328
		foreach ($nodes as $n) {
329
			$stack[] = $n;
330
			if (!empty($last)) {
331
				$end = end($stack);
332
				if ($end[$class]['rght'] > $last) {
333
					foreach ($stack as $k => $v) {
334
						$end = end($stack);
335
						if ($v[$class]['rght'] < $end[$class]['rght']) {
336
							unset($stack[$k]);
337
						}
338
					}
339
				}
340
			}
341
			$last = $n[$class]['rght'];
342
			$count = count($stack);
343
 
344
			$this->_outputNode($class, $n, $count);
345
		}
346
		$this->hr();
347
	}
348
 
349
/**
350
 * Initialize ACL database.
351
 *
352
 * @return mixed
353
 */
354
	public function initdb() {
355
		return $this->dispatchShell('schema create DbAcl');
356
	}
357
 
358
/**
359
 * Get the option parser.
360
 *
361
 * @return void
362
 */
363
	public function getOptionParser() {
364
		$parser = parent::getOptionParser();
365
 
366
		$type = array(
367
			'choices' => array('aro', 'aco'),
368
			'required' => true,
369
			'help' => __d('cake_console', 'Type of node to create.')
370
		);
371
 
372
		$parser->description(
373
			__d('cake_console', 'A console tool for managing the DbAcl')
374
			)->addSubcommand('create', array(
375
				'help' => __d('cake_console', 'Create a new ACL node'),
376
				'parser' => array(
377
					'description' => __d('cake_console', 'Creates a new ACL object <node> under the parent'),
378
					'epilog' => __d('cake_console', 'You can use `root` as the parent when creating nodes to create top level nodes.'),
379
					'arguments' => array(
380
						'type' => $type,
381
						'parent' => array(
382
							'help' => __d('cake_console', 'The node selector for the parent.'),
383
							'required' => true
384
						),
385
						'alias' => array(
386
							'help' => __d('cake_console', 'The alias to use for the newly created node.'),
387
							'required' => true
388
						)
389
					)
390
				)
391
			))->addSubcommand('delete', array(
392
				'help' => __d('cake_console', 'Deletes the ACL object with the given <node> reference'),
393
				'parser' => array(
394
					'description' => __d('cake_console', 'Delete an ACL node.'),
395
					'arguments' => array(
396
						'type' => $type,
397
						'node' => array(
398
							'help' => __d('cake_console', 'The node identifier to delete.'),
399
							'required' => true,
400
						)
401
					)
402
				)
403
			))->addSubcommand('setparent', array(
404
				'help' => __d('cake_console', 'Moves the ACL node under a new parent.'),
405
				'parser' => array(
406
					'description' => __d('cake_console', 'Moves the ACL object specified by <node> beneath <parent>'),
407
					'arguments' => array(
408
						'type' => $type,
409
						'node' => array(
410
							'help' => __d('cake_console', 'The node to move'),
411
							'required' => true,
412
						),
413
						'parent' => array(
414
							'help' => __d('cake_console', 'The new parent for <node>.'),
415
							'required' => true
416
						)
417
					)
418
				)
419
			))->addSubcommand('getpath', array(
420
				'help' => __d('cake_console', 'Print out the path to an ACL node.'),
421
				'parser' => array(
422
					'description' => array(
423
						__d('cake_console', "Returns the path to the ACL object specified by <node>."),
424
						__d('cake_console', "This command is useful in determining the inheritance of permissions for a certain object in the tree.")
425
					),
426
					'arguments' => array(
427
						'type' => $type,
428
						'node' => array(
429
							'help' => __d('cake_console', 'The node to get the path of'),
430
							'required' => true,
431
						)
432
					)
433
				)
434
			))->addSubcommand('check', array(
435
				'help' => __d('cake_console', 'Check the permissions between an ACO and ARO.'),
436
				'parser' => array(
437
					'description' => array(
438
						__d('cake_console', 'Use this command to check ACL permissions.')
439
					),
440
					'arguments' => array(
441
						'aro' => array('help' => __d('cake_console', 'ARO to check.'), 'required' => true),
442
						'aco' => array('help' => __d('cake_console', 'ACO to check.'), 'required' => true),
443
						'action' => array('help' => __d('cake_console', 'Action to check'), 'default' => 'all')
444
					)
445
				)
446
			))->addSubcommand('grant', array(
447
				'help' => __d('cake_console', 'Grant an ARO permissions to an ACO.'),
448
				'parser' => array(
449
					'description' => array(
450
						__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).')
451
					),
452
					'arguments' => array(
453
						'aro' => array('help' => __d('cake_console', 'ARO to grant permission to.'), 'required' => true),
454
						'aco' => array('help' => __d('cake_console', 'ACO to grant access to.'), 'required' => true),
455
						'action' => array('help' => __d('cake_console', 'Action to grant'), 'default' => 'all')
456
					)
457
				)
458
			))->addSubcommand('deny', array(
459
				'help' => __d('cake_console', 'Deny an ARO permissions to an ACO.'),
460
				'parser' => array(
461
					'description' => array(
462
						__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).')
463
					),
464
					'arguments' => array(
465
						'aro' => array('help' => __d('cake_console', 'ARO to deny.'), 'required' => true),
466
						'aco' => array('help' => __d('cake_console', 'ACO to deny.'), 'required' => true),
467
						'action' => array('help' => __d('cake_console', 'Action to deny'), 'default' => 'all')
468
					)
469
				)
470
			))->addSubcommand('inherit', array(
471
				'help' => __d('cake_console', 'Inherit an ARO\'s parent permissions.'),
472
				'parser' => array(
473
					'description' => array(
474
						__d('cake_console', "Use this command to force a child ARO object to inherit its permissions settings from its parent.")
475
					),
476
					'arguments' => array(
477
						'aro' => array('help' => __d('cake_console', 'ARO to have permissions inherit.'), 'required' => true),
478
						'aco' => array('help' => __d('cake_console', 'ACO to inherit permissions on.'), 'required' => true),
479
						'action' => array('help' => __d('cake_console', 'Action to inherit'), 'default' => 'all')
480
					)
481
				)
482
			))->addSubcommand('view', array(
483
				'help' => __d('cake_console', 'View a tree or a single node\'s subtree.'),
484
				'parser' => array(
485
					'description' => array(
486
						__d('cake_console', "The view command will return the ARO or ACO tree."),
487
						__d('cake_console', "The optional node parameter allows you to return"),
488
						__d('cake_console', "only a portion of the requested tree.")
489
					),
490
					'arguments' => array(
491
						'type' => $type,
492
						'node' => array('help' => __d('cake_console', 'The optional node to view the subtree of.'))
493
					)
494
				)
495
			))->addSubcommand('initdb', array(
496
				'help' => __d('cake_console', 'Initialize the DbAcl tables. Uses this command : cake schema create DbAcl')
497
			))->epilog(
498
				array(
499
					'Node and parent arguments can be in one of the following formats:',
500
					'',
501
					' - <model>.<id> - The node will be bound to a specific record of the given model.',
502
					'',
503
					' - <alias> - The node will be given a string alias (or path, in the case of <parent>)',
504
					"   i.e. 'John'. When used with <parent>, this takes the form of an alias path,",
505
					"   i.e. <group>/<subgroup>/<parent>.",
506
					'',
507
					"To add a node at the root level, enter 'root' or '/' as the <parent> parameter."
508
				)
509
			);
510
		return $parser;
511
	}
512
 
513
/**
514
 * Checks that given node exists
515
 *
516
 * @return boolean Success
517
 */
518
	public function nodeExists() {
519
		if (!isset($this->args[0]) || !isset($this->args[1])) {
520
			return false;
521
		}
522
		$dataVars = $this->_dataVars($this->args[0]);
523
		extract($dataVars);
524
		$key = is_numeric($this->args[1]) ? $dataVars['secondary_id'] : 'alias';
525
		$conditions = array($class . '.' . $key => $this->args[1]);
526
		$possibility = $this->Acl->{$class}->find('all', compact('conditions'));
527
		if (empty($possibility)) {
528
			$this->error(__d('cake_console', '%s not found', $this->args[1]), __d('cake_console', 'No tree returned.'));
529
		}
530
		return $possibility;
531
	}
532
 
533
/**
534
 * Parse an identifier into Model.foreignKey or an alias.
535
 * Takes an identifier determines its type and returns the result as used by other methods.
536
 *
537
 * @param string $identifier Identifier to parse
538
 * @return mixed a string for aliases, and an array for model.foreignKey
539
 */
540
	public function parseIdentifier($identifier) {
541
		if (preg_match('/^([\w]+)\.(.*)$/', $identifier, $matches)) {
542
			return array(
543
				'model' => $matches[1],
544
				'foreign_key' => $matches[2],
545
			);
546
		}
547
		return $identifier;
548
	}
549
 
550
/**
551
 * Get the node for a given identifier. $identifier can either be a string alias
552
 * or an array of properties to use in AcoNode::node()
553
 *
554
 * @param string $class Class type you want (Aro/Aco)
555
 * @param string|array $identifier A mixed identifier for finding the node.
556
 * @return integer Integer of NodeId. Will trigger an error if nothing is found.
557
 */
558
	protected function _getNodeId($class, $identifier) {
559
		$node = $this->Acl->{$class}->node($identifier);
560
		if (empty($node)) {
561
			if (is_array($identifier)) {
562
				$identifier = var_export($identifier, true);
563
			}
564
			$this->error(__d('cake_console', 'Could not find node using reference "%s"', $identifier));
565
			return;
566
		}
567
		return Hash::get($node, "0.{$class}.id");
568
	}
569
 
570
/**
571
 * get params for standard Acl methods
572
 *
573
 * @return array aro, aco, action
574
 */
575
	protected function _getParams() {
576
		$aro = is_numeric($this->args[0]) ? intval($this->args[0]) : $this->args[0];
577
		$aco = is_numeric($this->args[1]) ? intval($this->args[1]) : $this->args[1];
578
		$aroName = $aro;
579
		$acoName = $aco;
580
 
581
		if (is_string($aro)) {
582
			$aro = $this->parseIdentifier($aro);
583
		}
584
		if (is_string($aco)) {
585
			$aco = $this->parseIdentifier($aco);
586
		}
587
		$action = '*';
588
		if (isset($this->args[2]) && !in_array($this->args[2], array('', 'all'))) {
589
			$action = $this->args[2];
590
		}
591
		return compact('aro', 'aco', 'action', 'aroName', 'acoName');
592
	}
593
 
594
/**
595
 * Build data parameters based on node type
596
 *
597
 * @param string $type Node type  (ARO/ACO)
598
 * @return array Variables
599
 */
600
	protected function _dataVars($type = null) {
601
		if (!$type) {
602
			$type = $this->args[0];
603
		}
604
		$vars = array();
605
		$class = ucwords($type);
606
		$vars['secondary_id'] = (strtolower($class) === 'aro') ? 'foreign_key' : 'object_id';
607
		$vars['data_name'] = $type;
608
		$vars['table_name'] = $type . 's';
609
		$vars['class'] = $class;
610
		return $vars;
611
	}
612
 
613
}