1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 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: 27: 28: 29: 30:
31: class AclShell extends AppShell {
32:
33: 34: 35: 36: 37:
38: public $Acl;
39:
40: 41: 42: 43: 44:
45: public $args;
46:
47: 48: 49: 50: 51:
52: public $connection = 'default';
53:
54: 55: 56: 57: 58:
59: public $tasks = array('DbConfig');
60:
61: 62: 63: 64: 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: 105: 106: 107:
108: public function main() {
109: $this->out($this->OptionParser->help());
110: }
111:
112: 113: 114: 115: 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: 147: 148: 149: 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: 171: 172: 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: 195: 196: 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: 220: 221: 222: 223: 224: 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: 238: 239: 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: 253: 254: 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: 268: 269: 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: 283: 284: 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: 298: 299: 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: 358: 359: 360:
361: public function initdb() {
362: return $this->dispatchShell('schema create DbAcl');
363: }
364:
365: 366: 367: 368: 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(
505: array(
506: 'Node and parent arguments can be in one of the following formats:',
507: '',
508: ' - <model>.<id> - The node will be bound to a specific record of the given model.',
509: '',
510: ' - <alias> - The node will be given a string alias (or path, in the case of <parent>)',
511: " i.e. 'John'. When used with <parent>, this takes the form of an alias path,",
512: " i.e. <group>/<subgroup>/<parent>.",
513: '',
514: "To add a node at the root level, enter 'root' or '/' as the <parent> parameter."
515: )
516: );
517: return $parser;
518: }
519:
520: 521: 522: 523: 524:
525: public function nodeExists() {
526: if (!isset($this->args[0]) || !isset($this->args[1])) {
527: return false;
528: }
529: $dataVars = $this->_dataVars($this->args[0]);
530: extract($dataVars);
531: $key = is_numeric($this->args[1]) ? $dataVars['secondary_id'] : 'alias';
532: $conditions = array($class . '.' . $key => $this->args[1]);
533: $possibility = $this->Acl->{$class}->find('all', compact('conditions'));
534: if (empty($possibility)) {
535: $this->error(__d('cake_console', '%s not found', $this->args[1]), __d('cake_console', 'No tree returned.'));
536: }
537: return $possibility;
538: }
539:
540: 541: 542: 543: 544: 545: 546:
547: public function parseIdentifier($identifier) {
548: if (preg_match('/^([\w]+)\.(.*)$/', $identifier, $matches)) {
549: return array(
550: 'model' => $matches[1],
551: 'foreign_key' => $matches[2],
552: );
553: }
554: return $identifier;
555: }
556:
557: 558: 559: 560: 561: 562: 563: 564:
565: protected function _getNodeId($class, $identifier) {
566: $node = $this->Acl->{$class}->node($identifier);
567: if (empty($node)) {
568: if (is_array($identifier)) {
569: $identifier = var_export($identifier, true);
570: }
571: $this->error(__d('cake_console', 'Could not find node using reference "%s"', $identifier));
572: return;
573: }
574: return Hash::get($node, "0.{$class}.id");
575: }
576:
577: 578: 579: 580: 581:
582: protected function _getParams() {
583: $aro = is_numeric($this->args[0]) ? intval($this->args[0]) : $this->args[0];
584: $aco = is_numeric($this->args[1]) ? intval($this->args[1]) : $this->args[1];
585: $aroName = $aro;
586: $acoName = $aco;
587:
588: if (is_string($aro)) {
589: $aro = $this->parseIdentifier($aro);
590: }
591: if (is_string($aco)) {
592: $aco = $this->parseIdentifier($aco);
593: }
594: $action = '*';
595: if (isset($this->args[2]) && !in_array($this->args[2], array('', 'all'))) {
596: $action = $this->args[2];
597: }
598: return compact('aro', 'aco', 'action', 'aroName', 'acoName');
599: }
600:
601: 602: 603: 604: 605: 606:
607: protected function _dataVars($type = null) {
608: if (!$type) {
609: $type = $this->args[0];
610: }
611: $vars = array();
612: $class = ucwords($type);
613: $vars['secondary_id'] = (strtolower($class) === 'aro') ? 'foreign_key' : 'object_id';
614: $vars['data_name'] = $type;
615: $vars['table_name'] = $type . 's';
616: $vars['class'] = $class;
617: return $vars;
618: }
619:
620: }
621: