Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
16591 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. 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 $identifier A mixed identifier for finding the node.
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;
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
}