From 66c5312ea6cddb671d9477e3e906446aa38bfc67 Mon Sep 17 00:00:00 2001 From: noud Date: Thu, 28 Jun 2012 17:24:12 +0200 Subject: [PATCH 1/5] DataBase migrate, Audit and Access Control granulation. --- app/Config/Schema/db_group.php | 45 ++ app/Config/Schema/db_group.sql | 31 + app/Config/Schema/db_log.php | 28 + app/Config/Schema/db_log.sql | 21 + app/Config/Schema/schema_0.2.2.1.php | 81 +++ app/Config/Schema/schema_0.2.2.php | 73 +++ app/Config/Schema/schema_0.2.3.php | 101 +++ app/Config/bootstrap.php | 2 + app/Console/Command/Populate023Shell.php | 12 + app/Console/Command/Task/GroupIdTask.php | 14 + .../Command/Task/GroupToAroAcoTask.php | 18 + app/Console/Command/Task/GroupsTask.php | 24 + app/Console/shell/degrate-0.2.3-0.2.2.sh | 12 + app/Console/shell/migrate-0.2.2-0.2.3.sh | 29 + app/Console/shell/rights.sh | 12 + app/Controller/AppController.php | 26 +- app/Controller/AttributesController.php | 4 +- app/Controller/EventsController.php | 10 +- app/Controller/GroupsController.php | 177 ++++++ app/Controller/LogsController.php | 112 ++++ app/Controller/ServersController.php | 2 +- app/Controller/UsersController.php | 127 +++- app/Model/Attribute.php | 10 + app/Model/Event.php | 8 + app/Model/Group.php | 58 ++ app/Model/Log.php | 31 + app/Model/Server.php | 8 + app/Model/User.php | 57 ++ .../Console/Command/AclExtrasShell.php | 308 +++++++++ app/Plugin/AclExtras/README.md | 27 + .../Case/Console/Command/AclExtrasTest.php | 240 +++++++ .../AclExtras/Test/test_controllers.php | 53 ++ app/Plugin/SysLog/Lib/SysLog.php | 91 +++ app/README.txt | 36 +- app/README.ubuntu.txt | 34 + app/View/Attributes/edit.ctp | 16 +- app/View/Attributes/index.ctp | 406 +++++++++++- app/View/Elements/actions_menu.ctp | 9 +- app/View/Events/index.ctp | 591 +++++++++++++++++- app/View/Events/view.ctp | 166 ++++- app/View/Groups/add.ctp | 19 + app/View/Groups/admin_add.ctp | 19 + app/View/Groups/admin_edit.ctp | 23 + app/View/Groups/admin_index.ctp | 55 ++ app/View/Groups/admin_view.ctp | 47 ++ app/View/Groups/edit.ctp | 21 + app/View/Groups/index.ctp | 48 ++ app/View/Groups/view.ctp | 40 ++ app/View/Layouts/default.ctp | 2 + app/View/Logs/admin_index.ctp | 59 ++ app/View/Logs/admin_search.ctp | 53 ++ app/View/Logs/admin_view.ctp | 54 ++ app/View/Logs/index.ctp | 59 ++ app/View/Servers/index.ctp | 407 +++++++++++- app/View/Users/admin_add.ctp | 1 + app/View/Users/admin_edit.ctp | 1 + app/View/Users/admin_index.ctp | 410 +++++++++++- app/View/Users/admin_view.ctp | 30 +- app/View/Users/edit.ctp | 1 + app/View/Users/view.ctp | 5 + app/technical_design/TD-ACL.txt | 16 + app/technical_design/TD-Audit.txt | 28 + app/technical_design/TD-forum.txt | 12 + app/webroot/css/cake.generic.css | 8 + app/webroot/js/deactivateButtons.js | 101 +++ 65 files changed, 4584 insertions(+), 45 deletions(-) create mode 100755 app/Config/Schema/db_group.php create mode 100755 app/Config/Schema/db_group.sql create mode 100755 app/Config/Schema/db_log.php create mode 100755 app/Config/Schema/db_log.sql create mode 100755 app/Config/Schema/schema_0.2.2.1.php create mode 100755 app/Config/Schema/schema_0.2.2.php create mode 100755 app/Config/Schema/schema_0.2.3.php mode change 100644 => 100755 app/Config/bootstrap.php create mode 100755 app/Console/Command/Populate023Shell.php create mode 100755 app/Console/Command/Task/GroupIdTask.php create mode 100755 app/Console/Command/Task/GroupToAroAcoTask.php create mode 100755 app/Console/Command/Task/GroupsTask.php create mode 100755 app/Console/shell/degrate-0.2.3-0.2.2.sh create mode 100755 app/Console/shell/migrate-0.2.2-0.2.3.sh create mode 100755 app/Console/shell/rights.sh mode change 100644 => 100755 app/Controller/AppController.php mode change 100644 => 100755 app/Controller/AttributesController.php mode change 100644 => 100755 app/Controller/EventsController.php create mode 100755 app/Controller/GroupsController.php create mode 100755 app/Controller/LogsController.php mode change 100644 => 100755 app/Controller/ServersController.php mode change 100644 => 100755 app/Controller/UsersController.php mode change 100644 => 100755 app/Model/Attribute.php mode change 100644 => 100755 app/Model/Event.php create mode 100755 app/Model/Group.php create mode 100755 app/Model/Log.php mode change 100644 => 100755 app/Model/Server.php mode change 100644 => 100755 app/Model/User.php create mode 100755 app/Plugin/AclExtras/Console/Command/AclExtrasShell.php create mode 100755 app/Plugin/AclExtras/README.md create mode 100755 app/Plugin/AclExtras/Test/Case/Console/Command/AclExtrasTest.php create mode 100755 app/Plugin/AclExtras/Test/test_controllers.php create mode 100755 app/Plugin/SysLog/Lib/SysLog.php create mode 100755 app/README.ubuntu.txt create mode 100755 app/View/Groups/add.ctp create mode 100755 app/View/Groups/admin_add.ctp create mode 100755 app/View/Groups/admin_edit.ctp create mode 100755 app/View/Groups/admin_index.ctp create mode 100755 app/View/Groups/admin_view.ctp create mode 100755 app/View/Groups/edit.ctp create mode 100755 app/View/Groups/index.ctp create mode 100755 app/View/Groups/view.ctp mode change 100644 => 100755 app/View/Layouts/default.ctp create mode 100755 app/View/Logs/admin_index.ctp create mode 100755 app/View/Logs/admin_search.ctp create mode 100755 app/View/Logs/admin_view.ctp create mode 100755 app/View/Logs/index.ctp mode change 100644 => 100755 app/View/Servers/index.ctp mode change 100644 => 100755 app/View/Users/admin_add.ctp mode change 100644 => 100755 app/View/Users/admin_edit.ctp mode change 100644 => 100755 app/View/Users/admin_index.ctp mode change 100644 => 100755 app/View/Users/admin_view.ctp create mode 100755 app/technical_design/TD-ACL.txt create mode 100755 app/technical_design/TD-Audit.txt create mode 100755 app/technical_design/TD-forum.txt mode change 100644 => 100755 app/webroot/css/cake.generic.css create mode 100755 app/webroot/js/deactivateButtons.js diff --git a/app/Config/Schema/db_group.php b/app/Config/Schema/db_group.php new file mode 100755 index 000000000..7457d55eb --- /dev/null +++ b/app/Config/Schema/db_group.php @@ -0,0 +1,45 @@ +create(); +// $groups->save(array('Group' => array('name' => 'malware analyst', 'perm_add' => true, 'perm_modify' => true, 'perm_publish' => false, 'perm_full' => false))); +// $groups->create(); +// $groups->save(array('Group' => array('name' => 'admin', 'perm_add' => true, 'perm_modify' => true, 'perm_publish' => true, 'perm_full' => true))); +// $groups->create(); +// $groups->save(array('Group' => array('name' => 'IDS analyst', 'perm_add' => true, 'perm_modify' => true, 'perm_publish' => true, 'perm_full' => false))); +// $groups->create(); +// $groups->save(array('Group' => array('name' => 'guest', 'perm_add' => false, 'perm_modify' => false, 'perm_publish' => false, 'perm_full' => false))); + // populate Users.group_id +// $users = ClassRegistry::init('User'); +// $user = $users->read(null, '1'); +// $users->saveField('group_id', '2'); // $user['User']['group_id'] = '2'; + break; + } + } + } + + public $groups = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'latin1_swedish_ci', 'charset' => 'latin1'), + 'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL), + 'modified' => array('type' => 'datetime', 'null' => true, 'default' => NULL), + 'perm_add' => array('type' => 'boolean', 'null' => true, 'default' => NULL), + 'perm_modify' => array('type' => 'boolean', 'null' => true, 'default' => NULL), + 'perm_publish' => array('type' => 'boolean', 'null' => true, 'default' => NULL), + 'perm_full' => array('type' => 'boolean', 'null' => true, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)), + 'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'InnoDB') + ); +} diff --git a/app/Config/Schema/db_group.sql b/app/Config/Schema/db_group.sql new file mode 100755 index 000000000..87c3107c8 --- /dev/null +++ b/app/Config/Schema/db_group.sql @@ -0,0 +1,31 @@ +-- ACL, group table +-- works in conjunction with: CakePHP AclComponent + +CREATE TABLE groups ( + id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) NOT NULL, + perm_add boolean, + perm_modify boolean, + perm_publish boolean, + perm_full boolean, + created DATETIME, + modified DATETIME +); + +-- ALTER TABLE users ADD COLUMN group_id INT(11); + +-- data of Groups +-- INSERT INTO groups (name,perm_add,perm_modify,perm_publish,perm_full) VALUES ('malware analyst',true,true,false,false); +-- INSERT INTO groups (name,perm_add,perm_modify,perm_publish,perm_full) VALUES ('admin',true,true,true,true); +-- INSERT INTO groups (name,perm_add,perm_modify,perm_publish,perm_full) VALUES ('IDS analyst',true,true,true,false); +-- INSERT INTO groups (name,perm_add,perm_modify,perm_publish,perm_full) VALUES ('guest',false,false,false,false); + +-- CakePHP AclComponent acor & aros tables + +-- aros table (should be auto generated on group create) +-- INSERT INTO aros (model,foreign_key,lft,rght) VALUES ('Group',1,1,2); +-- INSERT INTO aros (model,foreign_key,lft,rght) VALUES ('Group',2,3,4); +-- INSERT INTO aros (model,foreign_key,lft,rght) VALUES ('Group',3,5,6); +-- INSERT INTO aros (model,foreign_key,lft,rght) VALUES ('Group',4,7,8); + +-- aros_acos diff --git a/app/Config/Schema/db_log.php b/app/Config/Schema/db_log.php new file mode 100755 index 000000000..a35e515a2 --- /dev/null +++ b/app/Config/Schema/db_log.php @@ -0,0 +1,28 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'title' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL), + 'model' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 20, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'model_id' => array('type' => 'integer', 'null' => true, 'default' => NULL), + 'action' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 20, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'user_id' => array('type' => 'integer', 'null' => true, 'default' => NULL), + 'change' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'email' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'org' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'description' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); +} diff --git a/app/Config/Schema/db_log.sql b/app/Config/Schema/db_log.sql new file mode 100755 index 000000000..226717d72 --- /dev/null +++ b/app/Config/Schema/db_log.sql @@ -0,0 +1,21 @@ +-- Audit, log table +-- works in conjunction with: +-- https://github.com/alkemann/CakePHP-Assets/wiki +-- also described at: +-- http://bakery.cakephp.org/articles/alkemann/2008/10/21/logablebehavior + +DROP TABLE logs; +CREATE TABLE logs ( + id int(11) NOT NULL AUTO_INCREMENT, + title varchar(255), + created DATETIME, + description varchar(255), + model varchar(20), + model_id int(11), + action varchar(20), + user_id int(11), + `change` varchar(255), + email varchar(255), + org varchar(255) COLLATE utf8_bin, + PRIMARY KEY (id) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=2 ; \ No newline at end of file diff --git a/app/Config/Schema/schema_0.2.2.1.php b/app/Config/Schema/schema_0.2.2.1.php new file mode 100755 index 000000000..bc532f04e --- /dev/null +++ b/app/Config/Schema/schema_0.2.2.1.php @@ -0,0 +1,81 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'event_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'index'), + 'type' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8'), + 'category' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'value1' => array('type' => 'text', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'to_ids' => array('type' => 'boolean', 'null' => false, 'default' => '1'), + 'uuid' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'revision' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => 10), + 'private' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'value2' => array('type' => 'text', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'event_id' => array('column' => 'event_id', 'unique' => 0), 'uuid' => array('column' => 'uuid', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $bruteforces = array( + 'ip' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'username' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'expire' => array('type' => 'datetime', 'null' => false, 'default' => NULL), + 'indexes' => array(), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $events = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'date' => array('type' => 'date', 'null' => false, 'default' => NULL), + 'info' => array('type' => 'text', 'null' => false, 'default' => NULL, 'key' => 'index', 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8'), + 'user_id' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'alerted' => array('type' => 'boolean', 'null' => false, 'default' => '0'), + 'uuid' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'private' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'published' => array('type' => 'boolean', 'null' => false, 'default' => '0'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'uuid' => array('column' => 'uuid', 'unique' => 0), 'info' => array('column' => 'info', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $servers = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'url' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'authkey' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'push' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'pull' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'lastfetchedid' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $users = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'password' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'email' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'autoalert' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'authkey' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'invited_by' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'gpgkey' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'nids_sid' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 15), + 'termsaccepted' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'newsread' => array('type' => 'date', 'null' => false, 'default' => NULL), + 'group_id' => array('type' => 'integer', 'null' => true, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'username' => array('column' => 'password', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); +} diff --git a/app/Config/Schema/schema_0.2.2.php b/app/Config/Schema/schema_0.2.2.php new file mode 100755 index 000000000..ffcb924dc --- /dev/null +++ b/app/Config/Schema/schema_0.2.2.php @@ -0,0 +1,73 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'event_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'index'), + 'type' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8'), + 'category' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'value1' => array('type' => 'text', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'to_ids' => array('type' => 'boolean', 'null' => false, 'default' => '1'), + 'uuid' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'revision' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => 10), + 'private' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'value2' => array('type' => 'text', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'event_id' => array('column' => 'event_id', 'unique' => 0), 'uuid' => array('column' => 'uuid', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $bruteforces = array( + 'ip' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'username' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'expire' => array('type' => 'datetime', 'null' => false, 'default' => NULL), + 'indexes' => array(), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $events = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'date' => array('type' => 'date', 'null' => false, 'default' => NULL), + 'info' => array('type' => 'text', 'null' => false, 'default' => NULL, 'key' => 'index', 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8'), + 'user_id' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'alerted' => array('type' => 'boolean', 'null' => false, 'default' => '0'), + 'uuid' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'private' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'published' => array('type' => 'boolean', 'null' => false, 'default' => '0'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'uuid' => array('column' => 'uuid', 'unique' => 0), 'info' => array('column' => 'info', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $servers = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'url' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'authkey' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'push' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'pull' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'lastfetchedid' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $users = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'password' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'email' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'autoalert' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'authkey' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'invited_by' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'gpgkey' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'nids_sid' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 15), + 'termsaccepted' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'newsread' => array('type' => 'date', 'null' => false, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'username' => array('column' => 'password', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); +} diff --git a/app/Config/Schema/schema_0.2.3.php b/app/Config/Schema/schema_0.2.3.php new file mode 100755 index 000000000..dad2bf3e6 --- /dev/null +++ b/app/Config/Schema/schema_0.2.3.php @@ -0,0 +1,101 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'event_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'index'), + 'type' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8'), + 'category' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'value1' => array('type' => 'text', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'to_ids' => array('type' => 'boolean', 'null' => false, 'default' => '1'), + 'uuid' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'revision' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => 10), + 'private' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'value2' => array('type' => 'text', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'event_id' => array('column' => 'event_id', 'unique' => 0), 'uuid' => array('column' => 'uuid', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $bruteforces = array( + 'ip' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'username' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'expire' => array('type' => 'datetime', 'null' => false, 'default' => NULL), + 'indexes' => array(), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $events = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'date' => array('type' => 'date', 'null' => false, 'default' => NULL), + 'info' => array('type' => 'text', 'null' => false, 'default' => NULL, 'key' => 'index', 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8'), + 'user_id' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'alerted' => array('type' => 'boolean', 'null' => false, 'default' => '0'), + 'uuid' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'private' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'published' => array('type' => 'boolean', 'null' => false, 'default' => '0'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'uuid' => array('column' => 'uuid', 'unique' => 0), 'info' => array('column' => 'info', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $groups = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'latin1_swedish_ci', 'charset' => 'latin1'), + 'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL), + 'modified' => array('type' => 'datetime', 'null' => true, 'default' => NULL), + 'perm_add' => array('type' => 'boolean', 'null' => true, 'default' => NULL), + 'perm_modify' => array('type' => 'boolean', 'null' => true, 'default' => NULL), + 'perm_publish' => array('type' => 'boolean', 'null' => true, 'default' => NULL), + 'perm_full' => array('type' => 'boolean', 'null' => true, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)), + 'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'InnoDB') + ); + public $logs = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'title' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL), + 'model' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 20, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'model_id' => array('type' => 'integer', 'null' => true, 'default' => NULL), + 'action' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 20, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'user_id' => array('type' => 'integer', 'null' => true, 'default' => NULL), + 'change' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'email' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'org' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'description' => array('type' => 'string', 'null' => true, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $servers = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'url' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'authkey' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'push' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'pull' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'lastfetchedid' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + public $users = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'password' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'email' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'autoalert' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'authkey' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'invited_by' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'gpgkey' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'nids_sid' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 15), + 'termsaccepted' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'newsread' => array('type' => 'date', 'null' => false, 'default' => NULL), + 'group_id' => array('type' => 'integer', 'null' => true, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'username' => array('column' => 'password', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); +} diff --git a/app/Config/bootstrap.php b/app/Config/bootstrap.php old mode 100644 new mode 100755 index 785fceec2..9a30d725c --- a/app/Config/bootstrap.php +++ b/app/Config/bootstrap.php @@ -142,3 +142,5 @@ Configure::write('SecureAuth.expire', 300); // the time-window for th * CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit * */ +CakePlugin::load('SysLog'); +CakePlugin::load('AclExtras'); \ No newline at end of file diff --git a/app/Console/Command/Populate023Shell.php b/app/Console/Command/Populate023Shell.php new file mode 100755 index 000000000..200b355e0 --- /dev/null +++ b/app/Console/Command/Populate023Shell.php @@ -0,0 +1,12 @@ +Groups->execute(); + $this->GroupId->execute('2'); + $this->GroupToAroAco->execute(); + } +} \ No newline at end of file diff --git a/app/Console/Command/Task/GroupIdTask.php b/app/Console/Command/Task/GroupIdTask.php new file mode 100755 index 000000000..14868e312 --- /dev/null +++ b/app/Console/Command/Task/GroupIdTask.php @@ -0,0 +1,14 @@ +Users = new UsersController(); + $this->Users->constructClasses(); + $this->Users->setgroupid($fk); + } +} \ No newline at end of file diff --git a/app/Console/Command/Task/GroupToAroAcoTask.php b/app/Console/Command/Task/GroupToAroAcoTask.php new file mode 100755 index 000000000..da7f05849 --- /dev/null +++ b/app/Console/Command/Task/GroupToAroAcoTask.php @@ -0,0 +1,18 @@ +Groups = new GroupsController(); + $this->Groups->constructClasses(); + + $groups = $this->Group->find('all'); + foreach ($groups as $group) { + $this->Groups->saveAcl(array('model' => 'Group', 'foreign_key' => $group['Group']['id']), $group['Group']['perm_add'], $group['Group']['perm_modify'], $group['Group']['perm_publish']); + } + } +} \ No newline at end of file diff --git a/app/Console/Command/Task/GroupsTask.php b/app/Console/Command/Task/GroupsTask.php new file mode 100755 index 000000000..ac243f089 --- /dev/null +++ b/app/Console/Command/Task/GroupsTask.php @@ -0,0 +1,24 @@ +Groups = new GroupsController(); + $this->Groups->constructClasses(); + + $groups = ClassRegistry::init('Group'); + $groups->create(); + $groups->save(array('Group' => array('name' => 'malware analyst', 'perm_add' => true, 'perm_modify' => true, 'perm_publish' => false, 'perm_full' => false))); + $groups->create(); + $groups->save(array('Group' => array('name' => 'admin', 'perm_add' => true, 'perm_modify' => true, 'perm_publish' => true, 'perm_full' => true))); + $groups->create(); + $groups->save(array('Group' => array('name' => 'IDS analyst', 'perm_add' => true, 'perm_modify' => true, 'perm_publish' => true, 'perm_full' => false))); + $groups->create(); + $groups->save(array('Group' => array('name' => 'guest', 'perm_add' => false, 'perm_modify' => false, 'perm_publish' => false, 'perm_full' => false))); + + } +} \ No newline at end of file diff --git a/app/Console/shell/degrate-0.2.3-0.2.2.sh b/app/Console/shell/degrate-0.2.3-0.2.2.sh new file mode 100755 index 000000000..a0b4f0268 --- /dev/null +++ b/app/Console/shell/degrate-0.2.3-0.2.2.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# degrate 0.2.3 to 0.2.2 + +# step into project and .. +PRJCT=/var/www/cydefsig/app +cd ${PRJCT} + +# update Schema, remove Users.group_id +./Console/cake schema update -s 0.2.2 + +exit 0; \ No newline at end of file diff --git a/app/Console/shell/migrate-0.2.2-0.2.3.sh b/app/Console/shell/migrate-0.2.2-0.2.3.sh new file mode 100755 index 000000000..9501f10fe --- /dev/null +++ b/app/Console/shell/migrate-0.2.2-0.2.3.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# migrate 0.2.2 to 0.2.3 + +# DataBase migrate, Audit and Access Control granulation + +# step into project and .. +PRJCT=/var/www/cydefsig/app +cd ${PRJCT} + +# create ACL tables +./Console/cake schema create DbAcl +# populate ACL acos +./Console/cake acl create aco root controllers +./Console/cake AclExtras.AclExtras aco_sync + +# update Schema, add Users.group_id +./Console/cake schema update -s 0.2.2.1 + +# create Log table +./Console/cake schema create DbLog + +# create Groups, populate ACL aros and Users.group_id +./Console/cake schema create DbGroup + +# populate 0.2.3 +./Console/cake populate0_2_3 + +exit 0; \ No newline at end of file diff --git a/app/Console/shell/rights.sh b/app/Console/shell/rights.sh new file mode 100755 index 000000000..101411039 --- /dev/null +++ b/app/Console/shell/rights.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +USER=noud + +chown -R ${USER}:www-data /var/www/cydefsig +chmod -R 750 /var/www/cydefsig +chmod -R g+s /var/www/cydefsig +cd /var/www/cydefsig/app/ +chmod -R g+w tmp +chmod -R g+w files + +exit 0 diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php old mode 100644 new mode 100755 index 15a88d9b0..4f95269d8 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -22,7 +22,6 @@ App::uses('Controller', 'Controller'); App::uses('Sanitize', 'Utility'); - /** * Application Controller * @@ -35,6 +34,7 @@ App::uses('Sanitize', 'Utility'); class AppController extends Controller { public $components = array( + 'Acl', // TODO XXX remove 'Session', 'Auth' => array( 'className' => 'SecureAuth', @@ -46,7 +46,8 @@ class AppController extends Controller { 'authError' => 'Did you really think you are allowed to see that?', 'loginRedirect' => array('controller' => 'users', 'action' => 'routeafterlogin'), 'logoutRedirect' => array('controller' => 'users', 'action' => 'login'), - 'authorize' => array('Controller') // Added this line + 'authorize' => array('Controller', // Added this line + 'Actions' => array('actionPath' => 'controllers')) // TODO ACL, 4: tell actionPath ) ); @@ -92,6 +93,11 @@ class AppController extends Controller { // These variables are required for every view $this->set('me', $this->Auth->user()); $this->set('isAdmin', $this->_isAdmin()); + + // TODO ACL: 5: from Controller to Views + $this->set('isAclAdd', $this->checkAcl('add')); + $this->set('isAclModify', $this->checkAcl('edit')); + $this->set('isAclPublish', $this->checkAcl('publish')); } @@ -246,4 +252,20 @@ class AppController extends Controller { } + // TODO ACL, 6b: check on Group and per Model (not used) + function checkAccess() { + $aco = ucfirst($this->params['controller']); + $user = ClassRegistry::init('User')->findById($this->Auth->user('id')); + return $this->Acl->check($user, 'controllers/'.$aco, '*'); + } + + // TODO ACL, 6: check on Group and any Model + function checkAcl($action) { + $aco = 'Events'; // TODO ACL was 'Attributes' + $user = ClassRegistry::init('User')->findById($this->Auth->user('id')); + // TODO ACL, CHECK, below if indicates some wrong: Fatal error: Call to a member function check() on a non-object in /var/www/cydefsig/app/Controller/AppController.php on line 289 + if ($this->Acl) return $this->Acl->check($user, 'controllers/'.$aco.'/'.$action, '*'); + else return true; + } + } diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php old mode 100644 new mode 100755 index d6f71c125..1ad310240 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -10,7 +10,7 @@ App::uses('File', 'Utility'); */ class AttributesController extends AppController { - public $components = array('Security', 'RequestHandler'); + public $components = array('Acl', 'Security', 'RequestHandler'); // XXX ACL component public $paginate = array( 'limit' => 60, 'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events @@ -33,7 +33,7 @@ class AttributesController extends AppController { return true; } // Only on own attributes for these actions - if (in_array($this->action, array('edit', 'delete'))) { + if (in_array($this->action, array('delete'))) { // TODO ACL, removed 'edit' override $attributeid = $this->request->params['pass'][0]; return $this->Attribute->isOwnedByOrg($attributeid, $this->Auth->user('org')); } diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php old mode 100644 new mode 100755 index 675a4abb2..6266971b1 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -16,6 +16,7 @@ class EventsController extends AppController { */ public $components = array( + 'Acl', // XXX ACL component 'Security', 'Email', 'RequestHandler', @@ -37,6 +38,13 @@ class EventsController extends AppController { $this->Auth->allow('text'); $this->Auth->allow('dot'); + + // TODO Audit, activate logable in a Controller + if (sizeof($this->uses) && $this->{$this->modelClass}->Behaviors->attached('Logable')) { + $this->{$this->modelClass}->setUserData($this->activeUser); + } + + // TODO ACL, if on ent/attr level, $this->set('isAcl', $this->checkAccess()); } public function isAuthorized($user) { @@ -45,7 +53,7 @@ class EventsController extends AppController { return true; } // Only on own events for these actions - if (in_array($this->action, array('edit', 'delete', 'alert', 'publish'))) { + if (in_array($this->action, array('alert'))) { // TODO ACL, CHECK, remove overruling 'edit', 'delete' and 'publish' $eventid = $this->request->params['pass'][0]; return $this->Event->isOwnedByOrg($eventid, $this->Auth->user('org')); } diff --git a/app/Controller/GroupsController.php b/app/Controller/GroupsController.php new file mode 100755 index 000000000..5fa3e0edf --- /dev/null +++ b/app/Controller/GroupsController.php @@ -0,0 +1,177 @@ + array( + 'authorize' => array( + 'Actions' => array('actionPath' => 'controllers/Groups') + ) + ), + 'Session' + ); + + //public $components = array('Security'); + public $paginate = array( + 'limit' => 60, + 'order' => array( + 'Group.name' => 'ASC' + ) + ); + + function beforeFilter() { + parent::beforeFilter(); + } + +/** + * view method + * + * @param string $id + * @return void + */ + public function view($id = null) { + $this->Group->id = $id; + if (!$this->Group->exists()) { + throw new NotFoundException(__('Invalid group')); + } + $this->set('group', $this->Group->read(null, $id)); + } + +/** + * admin_index method + * + * @return void + */ + public function admin_index() { + $this->Group->recursive = 0; + $this->set('groups', $this->paginate()); + } + +/** + * admin_view method + * + * @param string $id + * @return void + */ + public function admin_view($id = null) { + $this->Group->id = $id; + if (!$this->Group->exists()) { + throw new NotFoundException(__('Invalid group')); + } + $this->set('group', $this->Group->read(null, $id)); + } + +/** + * admin_add method + * + * @return void + */ + public function admin_add() { + if ($this->request->is('post')) { + $this->Group->create(); + if ($this->Group->save($this->request->data)) { + $this->Session->setFlash(__('The group has been saved')); + $this->redirect(array('action' => 'index')); + } else { + $this->Session->setFlash(__('The group could not be saved. Please, try again.')); + } + } else { + // generate auth key for a new user + //$newkey = $this->Group->generateAuthKey(); // TODO generateAuthKey? + //$this->set('authkey', $newkey); + } + } + +/** + * admin_edit method + * + * @param string $id + * @return void + */ + public function admin_edit($id = null) { + $this->Group->id = $id; + if (!$this->Group->exists()) { + throw new NotFoundException(__('Invalid group')); + } + if ($this->request->is('post') || $this->request->is('put')) { + $fields = array(); + foreach (array_keys($this->request->data['Group']) as $field) { + if($field != 'password') array_push($fields, $field); + } + if ("" != $this->request->data['Group']['password']) + $fields[] = 'password'; + if ($this->Group->save($this->request->data, true, $fields)) { + $this->saveAcl($this->Group, $this->data['Group']['perm_add'], $this->data['Group']['perm_modify'], $this->data['Group']['perm_publish']); // save to ACL as well + $this->Session->setFlash(__('The group has been saved')); + $this->_refreshAuth(); // in case we modify ourselves + $this->redirect(array('action' => 'index')); + } else { + $this->Session->setFlash(__('The group could not be saved. Please, try again.')); + } + } else { + $this->Group->recursive=0; + $this->Group->read(null, $id); + //$this->Group->set('password', ''); // TODO set password? + $this->request->data = $this->Group->data; + + } + } + +/** + * admin_delete method + * + * @param string $id + * @return void + */ + public function admin_delete($id = null) { + if (!$this->request->is('post')) { + throw new MethodNotAllowedException(); + } + $this->Group->id = $id; + if (!$this->Group->exists()) { + throw new NotFoundException(__('Invalid group')); + } + if ($this->Group->delete()) { + $this->Session->setFlash(__('Group deleted')); + $this->redirect(array('action' => 'index')); + } + $this->Session->setFlash(__('Group was not deleted')); + $this->redirect(array('action' => 'index')); + } + +/** + * saveAcl method + * + * @param string $id + * @return void + */ + public function saveAcl($group, $permAdd = false, $permModify = false, $permPublish = false) { + // this all could need some 'if-changed then do' + + if ($permAdd) { + $this->Acl->allow($group, 'controllers/Events/add'); + $this->Acl->allow($group, 'controllers/Attributes/add'); + } else { + $this->Acl->deny($group, 'controllers/Events/add'); + $this->Acl->deny($group, 'controllers/Attributes/add'); + } + if ($permModify) { + $this->Acl->allow($group, 'controllers/Events/edit'); + $this->Acl->allow($group, 'controllers/Attributes/edit'); + } else { + $this->Acl->deny($group, 'controllers/Events/edit'); + $this->Acl->deny($group, 'controllers/Attributes/edit'); + } + if ($permPublish) { + $this->Acl->allow($group, 'controllers/Events/publish'); + } else { + $this->Acl->deny($group, 'controllers/Events/publish'); + } + } +} diff --git a/app/Controller/LogsController.php b/app/Controller/LogsController.php new file mode 100755 index 000000000..a36810b12 --- /dev/null +++ b/app/Controller/LogsController.php @@ -0,0 +1,112 @@ + 60, + 'order' => array( + 'Log.id' => 'DESC' + ) + ); + public $helpers = array('Js' => array('Jquery')); + + function beforeFilter() { + parent::beforeFilter(); + + // permit reuse of CSRF tokens on the search page. + if ('search' == $this->request->params['action']) { + $this->Security->csrfUseOnce = false; + } + } + + public function isAuthorized($user) { + // Admins can access everything + if (parent::isAuthorized($user)) { + return true; + } + // the other pages are allowed by logged in users + return true; + } + +/** + * admin_index method + * + * @return void + */ + public function admin_index() { + $this->Log->recursive = 0; + $this->set('logs', $this->paginate()); + } + +/** + * admin_view method + * + * @param string $id + * @return void + */ + public function admin_view($id = null) { + $this->Log->id = $id; + if (!$this->Log->exists()) { + throw new NotFoundException(__('Invalid log')); + } + $this->set('log', $this->Log->read(null, $id)); + } + + public function search() { + $this->admin_search(); + } + + public function admin_search() { + + $this->set('action_definitions', $this->Log->action_definitions); + + if ($this->request->is('post')) { + $email = $this->request->data['Log']['email']; + $org = $this->request->data['Log']['org']; + $action = $this->request->data['Log']['action']; + $title = $this->request->data['Log']['title']; + $change = $this->request->data['Log']['change']; + + // search the db + $conditions = array(); + if($email) { + $conditions['Log.email LIKE'] = '%'.$email.'%'; + } + if($org) { + $conditions['Log.org LIKE'] = '%'.$org.'%'; + } + if($action != 'ALL') { + $conditions['Log.action ='] = $action; + } + if($title) { + $conditions['Log.title LIKE'] = '%'.$title.'%'; + } + if($change) { + $conditions['Log.change LIKE'] = '%'.$change.'%'; + } + $this->Log->recursive = 0; + $this->paginate = array( + 'conditions' => $conditions + ); + $this->set('logs', $this->paginate()); + + // set the same view as the index page + $this->render('index'); + } else { + // no search keyword is given, show the search form + + // combobox for actions + $actions = array('ALL'); + $actions = array_merge($actions, $this->Log->validate['action']['rule'][1]); + $actions = $this->_arrayToValuesIndexArray($actions); + $this->set('actions',compact('actions')); + } + } +} diff --git a/app/Controller/ServersController.php b/app/Controller/ServersController.php old mode 100644 new mode 100755 index ca0be4a8e..3ff2b0ee7 --- a/app/Controller/ServersController.php +++ b/app/Controller/ServersController.php @@ -9,7 +9,7 @@ App::uses('Xml', 'Utility'); */ class ServersController extends AppController { - public $components = array('Security' ,'RequestHandler'); + public $components = array('Acl' ,'Security' ,'RequestHandler'); // XXX ACL component public $paginate = array( 'limit' => 60, 'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php old mode 100644 new mode 100755 index b0ff283c3..a160e5d4b --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -8,7 +8,7 @@ App::uses('AppController', 'Controller'); class UsersController extends AppController { - public $components = array('Security'); + public $components = array('Acl','Security'); // TODO ACL, components public $paginate = array( 'limit' => 60, 'order' => array( @@ -74,7 +74,7 @@ class UsersController extends AppController { // Only own profile verified by isAuthorized if ($this->request->is('post') || $this->request->is('put')) { // What fields should be saved (allowed to be saved) - $fieldList=array('email', 'autoalert', 'gpgkey', 'nids_sid' ); + $fieldList=array('email', 'autoalert', 'gpgkey', 'nids_sid'); // TODO ACL, check, My Profile not edit group_id. if ("" != $this->request->data['User']['password']) $fieldList[] = 'password'; // Save the data @@ -92,6 +92,9 @@ class UsersController extends AppController { $this->request->data = $this->User->data; } $this->request->data['User']['org']=$this->Auth->user('org'); + // XXX ACL groups + $groups = $this->User->Group->find('list'); + $this->set(compact('groups')); } /** @@ -164,6 +167,9 @@ class UsersController extends AppController { $newkey = $this->User->generateAuthKey(); $this->set('authkey', $newkey); } + // XXX ACL groups + $groups = $this->User->Group->find('list'); + $this->set(compact('groups')); } /** @@ -182,9 +188,48 @@ class UsersController extends AppController { foreach (array_keys($this->request->data['User']) as $field) { if($field != 'password') array_push($fields, $field); } + // TODO Audit, extraLog, fields get orig + $fields_oldValues = array(); + foreach ($fields as $field) { + if($field != 'confirm_password') array_push($fields_oldValues, $this->User->field($field)); + else array_push($fields_oldValues, $this->User->field('password')); + } + // TODO Audit, extraLog, fields get orig END if ("" != $this->request->data['User']['password']) $fields[] = 'password'; if ($this->User->save($this->request->data, true, $fields)) { + // TODO Audit, extraLog, fields compare + // newValues to array + $fields_newValues = array(); + foreach ($fields as $field) { + if($field != 'confirm_password') { + $newValue = $this->data['User'][$field]; + if (gettype($newValue) == 'array') { + $newValueStr = ''; + $c_p = 0; + foreach ($newValue as $newValuePart) { + if ($c_p < 2) $newValueStr .= '-' . $newValuePart; + else $newValueStr = $newValuePart.$newValueStr; + $c_p++; + } + array_push($fields_newValues, $newValueStr); + } + else array_push($fields_newValues, $newValue); + } + else array_push($fields_newValues, $this->data['User']['password']); + } + // compare + $fields_result_str = ''; + $c = 0; + foreach ($fields as $field) { + if ($fields_oldValues[$c] != $fields_newValues[$c]) { + if($field != 'confirm_password') $fields_result_str = $fields_result_str. ', '.$field.' ('.$fields_oldValues[$c]. ') => ('.$fields_newValues[$c].')'; + } + $c++; + } + $fields_result_str = substr($fields_result_str, 2); + $this->extraLog("admin_modify", "user", $fields_result_str); // TODO Audit, check: modify User + // TODO Audit, extraLog, fields compare END $this->Session->setFlash(__('The user has been saved')); $this->_refreshAuth(); // in case we modify ourselves $this->redirect(array('action' => 'index')); @@ -198,6 +243,13 @@ class UsersController extends AppController { $this->request->data = $this->User->data; } + // TODO ACL CLEANUP combobox for orgs + $org_ids = array('ADMIN', 'NCIRC','Other MOD'); + $org_ids = $this->_arrayToValuesIndexArray($org_ids); + $this->set('org_ids',compact('org_ids')); + // XXX ACL, Groups in Users + $groups = $this->User->Group->find('list'); + $this->set(compact('groups')); } /** @@ -226,6 +278,7 @@ class UsersController extends AppController { public function login() { // FIXME implement authentication brute-force protection if ($this->Auth->login()) { + $this->extraLog("login"); // TODO Audit, extraLog, check: customLog i.s.o. extraLog, no auth user?: $this->User->customLog('login', $this->Auth->user('id'), array('title' => '','user_id' => $this->Auth->user('id'),'email' => $this->Auth->user('email'),'org' => 'IN2')); $this->redirect($this->Auth->redirect()); } else { // don't display "invalid user" before first login attempt @@ -241,7 +294,7 @@ class UsersController extends AppController { } // News page - $new_newsdate = new DateTime("2012-03-27"); + $new_newsdate = new DateTime("2012-03-27"); // TODO general, fixed odd date?? $newsdate = new DateTime($this->Auth->user('newsread')); if ($new_newsdate > $newsdate) { $this->redirect(array('action' => 'news')); @@ -252,6 +305,7 @@ class UsersController extends AppController { } public function logout() { + $this->extraLog("logout"); // TODO Audit, extraLog, check: customLog i.s.o. extraLog, $this->User->customLog('logout', $this->Auth->user('id'), array()); $this->Session->setFlash('Good-Bye'); $this->redirect($this->Auth->logout()); } @@ -349,7 +403,74 @@ class UsersController extends AppController { $this->_refreshAuth(); // refresh auth info } + public function extraLog($action = null, $description = null, $fields_result = null) { // TODO move audit to AuditsController? + // configuration + ClassRegistry::init('ConnectionManager'); + $dbh = ConnectionManager::getDataSource('default'); + $dbhost = $dbh->config['host']; + $dbport = $dbh->config['port']; + $dbname = $dbh->config['database']; + $dbuser = $dbh->config['login']; + $dbpass = $dbh->config['password']; + $dbprefix = $dbh->config['prefix']; // TODO Audit, extra, db prefix delimiter? + + // database connection + $conn = new PDO("mysql:host=$dbhost;port=$dbport;dbname=$dbname",$dbuser,$dbpass); + // new data + $user_id = $this->Auth->user('id'); + $model = 'User'; + $model_id = $this->Auth->user('id'); + $org = $this->Auth->user('org'); + $email = $this->Auth->user('email'); + $action_date = new DateTime(); + $action_date_str = $action_date->format('Y-m-d H:i:sP'); + $description = "User (". $this->Auth->user('id')."): " .$this->Auth->user('email'); + // query + $sql = "INSERT INTO ".$dbprefix."logs (org,email,created,action,title,`change`) VALUES (:org,:email,:created,:action,:title,:change)"; + $q = $conn->prepare($sql); + $q->execute(array(':org'=>$org, + ':email'=>$email, + ':created'=>$action_date_str, + ':action'=>$action, + ':title'=>$description, + ':change'=>$fields_result)); + // database connection disconnect + $dbh = null; + + // write to syslogd as well + $syslog = new SysLog(); + if ($fields_result) $syslog->write('notice', $description.' -- '.$action.' -- '.$fields_result); + else $syslog->write('notice', $description.' -- '.$action); + } + + // used for fields_before and fields for audit + public function arrayCopy( array $array ) { + $result = array(); + foreach( $array as $key => $val ) { + if( is_array( $val ) ) { + $result[$key] = arrayCopy( $val ); + } elseif ( is_object( $val ) ) { + $result[$key] = clone $val; + } else { + $result[$key] = $val; + } + } + return $result; + } + + public function setgroupid($fk = '2') { + $params = array( + 'conditions' => array('User.group_id' => ''), + 'recursive' => 0, + 'fields' => array('User.id'), + ); + $users = $this->User->find('all', $params); + foreach ($users as $user) { + $this->User->id = $user['User']['id']; + $this->User->saveField('group_id', $fk); + } + } } diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php old mode 100644 new mode 100755 index 7bb70d41e..b982df418 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -9,6 +9,16 @@ App::uses('File', 'Utility'); * @property Event $Event */ class Attribute extends AppModel { + + var $combinedKeys = array('event_id', 'category', 'type'); + + var $name = 'Attribute'; // TODO general + var $actsAs = array('Logable' => array( // TODO Audit, logable + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full' + )); + /** * Display field * diff --git a/app/Model/Event.php b/app/Model/Event.php old mode 100644 new mode 100755 index 6d4523a86..0f13361fc --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -7,6 +7,14 @@ App::uses('AppModel', 'Model'); * @property Attribute $Attribute */ class Event extends AppModel { + + var $name = 'Event'; // TODO general + var $actsAs = array('Logable' => array( // TODO Audit, logable + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full' + )); + /** * Display field * diff --git a/app/Model/Group.php b/app/Model/Group.php new file mode 100755 index 000000000..46cec76c7 --- /dev/null +++ b/app/Model/Group.php @@ -0,0 +1,58 @@ + array( + 'notempty' => array( + 'rule' => array('notempty'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + + //The Associations below have been created with all possible keys, those that are not needed can be removed + +/** + * hasMany associations + * + * @var array + */ + public $hasMany = array( + 'User' => array( + 'className' => 'User', + 'foreignKey' => 'group_id', + 'dependent' => false, + 'conditions' => '', + 'fields' => '', + 'order' => '', + 'limit' => '', + 'offset' => '', + 'exclusive' => '', + 'finderQuery' => '', + 'counterQuery' => '' + ) + ); + + // TODO ACL: 1: be requester to CakePHP ACL system + public $actsAs = array('Acl' => array('type' => 'requester')); + + // TODO ACL: 2: hook Group into CakePHP ACL system (so link to aros) + public function parentNode() { + return null; + } + +} diff --git a/app/Model/Log.php b/app/Model/Log.php new file mode 100755 index 000000000..ae40bc75b --- /dev/null +++ b/app/Model/Log.php @@ -0,0 +1,31 @@ + array( + 'rule' => array('inList', array( + 'login', + 'logout', + 'add', + 'edit', + 'delete', + 'publish' // FIXME remove this once all attributes have a category. Otherwise sigs without category are not shown in the list + )), + 'message' => 'Options : ...' + ) + ); + + public $action_definitions = array( + 'login' => array('desc' => 'Login action', 'formdesc' => "Login action"), + 'logout' => array('desc' => 'Logout action', 'formdesc' => "Logout action"), + 'add' => array('desc' => 'Add action', 'formdesc' => "Add action"), + 'edit' => array('desc' => 'Edit action', 'formdesc' => "Edit action"), + 'delete' => array('desc' => 'Delete action', 'formdesc' => "Delete action"), + 'publish' => array('desc' => "Publish action", 'formdesc' => "Publish action") + ); +} \ No newline at end of file diff --git a/app/Model/Server.php b/app/Model/Server.php old mode 100644 new mode 100755 index a7ebad244..b36e69f3b --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -5,6 +5,14 @@ App::uses('AppModel', 'Model'); * */ class Server extends AppModel { + + var $name = 'Server'; // TODO general + var $actsAs = array('Logable' => array( // TODO Audit, logable, check: 'userModel' and 'userKey' can be removed given default + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full' + )); + /** * Display field * diff --git a/app/Model/User.php b/app/Model/User.php old mode 100644 new mode 100755 index d482f088c..91e5fa9e6 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -15,6 +15,13 @@ class User extends AppModel { * @var string */ public $displayField = 'email'; + public $orgField = 'org'; // TODO Audit, LogableBehaviour + org +/** + * Model Name + * + * @var string + */ + var $name = 'User'; // TODO general /** * Validation rules * @@ -67,6 +74,16 @@ class User extends AppModel { //'on' => 'create', // Limit validation to 'create' or 'update' operations ), ), + 'org_id' => array( + 'notempty' => array( + 'rule' => array('notempty'), + 'message' => 'Please specify the organisation ID where you are working.', // TODO ACL, org_id in Users + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), 'email' => array( 'email' => array( 'rule' => array('email'), @@ -155,6 +172,21 @@ class User extends AppModel { //The Associations below have been created with all possible keys, those that are not needed can be removed +/** + * belongsTo associations + * + * @var array + */ + public $belongsTo = array( + 'Group' => array( + 'className' => 'Group', + 'foreignKey' => 'group_id', + 'conditions' => '', + 'fields' => '', + 'order' => '' + ) + ); + /** * hasMany associations * @@ -176,7 +208,32 @@ class User extends AppModel { ) ); + // TODO ACL: 1: be requester to CakePHP ACL system + public $actsAs = array('Acl' => array('type' => 'requester', 'enabled' => false)); // TODO ACL, + 'enabled' => false + + // TODO ACL: 2: hook User into CakePHP ACL system (so link to aros) + public function parentNode() { + if (!$this->id && empty($this->data)) { + return null; + } + if (isset($this->data['User']['group_id'])) { + $groupId = $this->data['User']['group_id']; + } else { + $groupId = $this->field('group_id'); + } + if (!$groupId) { + return null; + } else { + return array('Group' => array('id' => $groupId)); + } + } + // TODO ACL: 3: rights on Groups: http://stackoverflow.com/questions/6154285/aros-table-in-cakephp-is-still-including-users-even-after-bindnode + function bindNode($user) { + // return array('model' => 'Group', 'foreign_key' => $user['User']['group_id']); + return array('Group' => array('id' => $user['User']['group_id'])); + } + public function beforeSave() { if (isset($this->data[$this->alias]['password'])) { $this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']); diff --git a/app/Plugin/AclExtras/Console/Command/AclExtrasShell.php b/app/Plugin/AclExtras/Console/Command/AclExtrasShell.php new file mode 100755 index 000000000..7ac1a36f2 --- /dev/null +++ b/app/Plugin/AclExtras/Console/Command/AclExtrasShell.php @@ -0,0 +1,308 @@ + + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +App::uses('Controller', 'Controller'); +App::uses('ComponentCollection', 'Controller'); +App::uses('AclComponent', 'Controller/Component'); +App::uses('DbAcl', 'Model'); + +/** + * Shell for ACO extras + * + * @package acl_extras + * @subpackage acl_extras.Console.Command + */ +class AclExtrasShell extends Shell { +/** + * Contains instance of AclComponent + * + * @var AclComponent + * @access public + */ + public $Acl; + +/** + * Contains arguments parsed from the command line. + * + * @var array + * @access public + */ + public $args; + +/** + * Contains database source to use + * + * @var string + * @access public + */ + public $dataSource = 'default'; + +/** + * Root node name. + * + * @var string + **/ + public $rootNode = 'controllers'; + +/** + * Internal Clean Actions switch + * + * @var boolean + **/ + public $_clean = false; + +/** + * Start up And load Acl Component / Aco model + * + * @return void + **/ + public function startup() { + parent::startup(); + $collection = new ComponentCollection(); + $this->Acl = new AclComponent($collection); + $controller = null; + $this->Acl->startup($controller); + $this->Aco = $this->Acl->Aco; + } + +/** + * Sync the ACO table + * + * @return void + **/ + function aco_sync() { + $this->_clean = true; + $this->aco_update(); + } +/** + * Updates the Aco Tree with new controller actions. + * + * @return void + **/ + function aco_update() { + $root = $this->_checkNode($this->rootNode, $this->rootNode, null); + $controllers = $this->getControllerList(); + $this->_updateControllers($root, $controllers); + + $plugins = CakePlugin::loaded(); + foreach ($plugins as $plugin) { + $controllers = $this->getControllerList($plugin); + + $path = $this->rootNode . '/' . $plugin; + $pluginRoot = $this->_checkNode($path, $plugin, $root['Aco']['id']); + $this->_updateControllers($pluginRoot, $controllers, $plugin); + } + $this->out(__('Aco Update Complete')); + return true; + } + +/** + * Updates a collection of controllers. + * + * @param array $root Array or ACO information for root node. + * @param array $controllers Array of Controllers + * @param string $plugin Name of the plugin you are making controllers for. + * @return void + */ + function _updateControllers($root, $controllers, $plugin = null) { + $dotPlugin = $pluginPath = $plugin; + if ($plugin) { + $dotPlugin .= '.'; + $pluginPath .= '/'; + } + $appIndex = array_search($plugin . 'AppController', $controllers); + if ($appIndex !== false) { + App::uses($plugin . 'AppController', $dotPlugin . 'Controller'); + unset($controllers[$appIndex]); + } + // look at each controller + foreach ($controllers as $controller) { + App::uses($controller, $dotPlugin . 'Controller'); + $controllerName = preg_replace('/Controller$/', '', $controller); + + $path = $this->rootNode . '/' . $pluginPath . $controllerName; + $controllerNode = $this->_checkNode($path, $controllerName, $root['Aco']['id']); + $this->_checkMethods($controller, $controllerName, $controllerNode, $pluginPath); + } + if ($this->_clean) { + if (!$plugin) { + $controllers = array_merge($controllers, App::objects('plugin', null, false)); + } + $controllerFlip = array_flip($controllers); + + $this->Aco->id = $root['Aco']['id']; + $controllerNodes = $this->Aco->children(null, true); + foreach ($controllerNodes as $ctrlNode) { + $alias = $ctrlNode['Aco']['alias']; + $name = $alias . 'Controller'; + if (!isset($controllerFlip[$name]) && !isset($controllerFlip[$alias])) { + if ($this->Aco->delete($ctrlNode['Aco']['id'])) { + $this->out(__( + 'Deleted %s and all children', + $this->rootNode . '/' . $ctrlNode['Aco']['alias'] + ), 1, Shell::VERBOSE); + } + } + } + } + } + +/** + * Get a list of controllers in the app and plugins. + * + * Returns an array of path => import notation. + * + * @param string $plugin Name of plugin to get controllers for + * @return array + **/ + function getControllerList($plugin = null) { + if (!$plugin) { + $controllers = App::objects('Controller', null, false); + } else { + $controllers = App::objects($plugin . '.Controller', null, false); + } + return $controllers; + } + +/** + * Check a node for existance, create it if it doesn't exist. + * + * @param string $path + * @param string $alias + * @param int $parentId + * @return array Aco Node array + */ + function _checkNode($path, $alias, $parentId = null) { + $node = $this->Aco->node($path); + if (!$node) { + $this->Aco->create(array('parent_id' => $parentId, 'model' => null, 'alias' => $alias)); + $node = $this->Aco->save(); + $node['Aco']['id'] = $this->Aco->id; + $this->out(__('Created Aco node: %s', $path), 1, Shell::VERBOSE); + } else { + $node = $node[0]; + } + return $node; + } + +/** + * Check and Add/delete controller Methods + * + * @param string $controller + * @param array $node + * @param string $plugin Name of plugin + * @return void + */ + function _checkMethods($className, $controllerName, $node, $pluginPath = false) { + $baseMethods = get_class_methods('Controller'); + $actions = get_class_methods($className); + $methods = array_diff($actions, $baseMethods); + foreach ($methods as $action) { + if (strpos($action, '_', 0) === 0) { + continue; + } + $path = $this->rootNode . '/' . $pluginPath . $controllerName . '/' . $action; + $this->_checkNode($path, $action, $node['Aco']['id']); + } + + if ($this->_clean) { + $actionNodes = $this->Aco->children($node['Aco']['id']); + $methodFlip = array_flip($methods); + foreach ($actionNodes as $action) { + if (!isset($methodFlip[$action['Aco']['alias']])) { + $this->Aco->id = $action['Aco']['id']; + if ($this->Aco->delete()) { + $path = $this->rootNode . '/' . $controllerName . '/' . $action['Aco']['alias']; + $this->out(__('Deleted Aco node %s', $path), 1, Shell::VERBOSE); + } + } + } + } + return true; + } + + public function getOptionParser() { + return parent::getOptionParser() + ->description(__("Better manage, and easily synchronize you application's ACO tree")) + ->addSubcommand('aco_update', array( + 'help' => __('Add new ACOs for new controllers and actions. Does not remove nodes from the ACO table.') + ))->addSubcommand('aco_sync', array( + 'help' => __('Perform a full sync on the ACO table.' . + 'Will create new ACOs or missing controllers and actions.' . + 'Will also remove orphaned entries that no longer have a matching controller/action') + ))->addSubcommand('verify', array( + 'help' => __('Verify the tree structure of either your Aco or Aro Trees'), + 'parser' => array( + 'arguments' => array( + 'type' => array( + 'required' => true, + 'help' => __('The type of tree to verify'), + 'choices' => array('aco', 'aro') + ) + ) + ) + ))->addSubcommand('recover', array( + 'help' => __('Recover a corrupted Tree'), + 'parser' => array( + 'arguments' => array( + 'type' => array( + 'required' => true, + 'help' => __('The type of tree to recover'), + 'choices' => array('aco', 'aro') + ) + ) + ) + )); + } + +/** + * Verify a Acl Tree + * + * @param string $type The type of Acl Node to verify + * @access public + * @return void + */ + function verify() { + $type = Inflector::camelize($this->args[0]); + $return = $this->Acl->{$type}->verify(); + if ($return === true) { + $this->out(__('Tree is valid and strong')); + } else { + $this->err(print_r($return, true)); + return false; + } + } +/** + * Recover an Acl Tree + * + * @param string $type The Type of Acl Node to recover + * @access public + * @return void + */ + function recover() { + $type = Inflector::camelize($this->args[0]); + $return = $this->Acl->{$type}->recover(); + if ($return === true) { + $this->out(__('Tree has been recovered, or tree did not need recovery.')); + } else { + $this->err(__('Tree recovery failed.')); + return false; + } + } +} diff --git a/app/Plugin/AclExtras/README.md b/app/Plugin/AclExtras/README.md new file mode 100755 index 000000000..590216c60 --- /dev/null +++ b/app/Plugin/AclExtras/README.md @@ -0,0 +1,27 @@ +# Acl Extras + +Acl Extras provides a console app that helps you manage DbAcl records more easily. Its main feature and purpose is to make generating Aco nodes for all your controllers and actions easier. It also includes some helper methods for verifying and recovering corrupted trees. + +## Installation + +Clone the repo or download a tarball and install it into `app/Plugin/AclExtras` or in any of your pluginPaths. + +## Usage + +You can find a list of commands by running `Console/cake AclExtras.AclExtras -h` from your command line. + +### Setting up the contorller + +You'll need to configure AuthComponent to use the Actions authorization method. +In your `beforeFilter` add the following: + + $this->Auth->authorize = 'actions'; + $this->Auth->actionPath = 'controllers/'; + +## Issues + +If you find an issue in the code or want to suggest something, please use the tickets at http://github.com/markstory/acl_extras/issues + +## License + +Acl Extras is licensed under the MIT license. \ No newline at end of file diff --git a/app/Plugin/AclExtras/Test/Case/Console/Command/AclExtrasTest.php b/app/Plugin/AclExtras/Test/Case/Console/Command/AclExtrasTest.php new file mode 100755 index 000000000..83983ce0d --- /dev/null +++ b/app/Plugin/AclExtras/Test/Case/Console/Command/AclExtrasTest.php @@ -0,0 +1,240 @@ + + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::uses('Shell', 'Console'); +App::uses('Aco', 'Model'); +App::uses('AclComponent', 'Controller/Component'); +App::uses('Controller', 'Controller'); +App::uses('AclExtrasShell', 'AclExtras.Console/Command'); + + +//Mock::generate('Aco', 'MockAco', array('children', 'verify', 'recover')); + +//import test controller class names. +include dirname(dirname(dirname(dirname(__FILE__)))) . DS . 'test_controllers.php'; + +/** + * AclExtras Shell Test case + * + * @package acl_extras.tests.cases + */ +class AclExtrasShellTestCase extends CakeTestCase { + + public $fixtures = array('core.aco', 'core.aro', 'core.aros_aco'); + +/** + * startTest + * + * @return void + * @access public + */ + function setUp() { + parent::setUp(); + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'test'); + + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + + $this->Task = $this->getMock( + 'AclExtrasShell', + array('in', 'out', 'hr', 'createFile', 'error', 'err', 'clear', 'getControllerList'), + array($out, $out, $in) + ); + } + +/** + * end the test + * + * @return void + **/ + function tearDown() { + parent::tearDown(); + unset($this->Task); + } + +/** + * test recover + * + * @return void + **/ + function testRecover() { + $this->Task->startup(); + $this->Task->args = array('Aco'); + $this->Task->Acl->Aco = $this->getMock('Aco', array('recover')); + $this->Task->Acl->Aco->expects($this->once()) + ->method('recover') + ->will($this->returnValue(true)); + + $this->Task->expects($this->once()) + ->method('out') + ->with($this->matchesRegularExpression('/recovered/')); + + $this->Task->recover(); + } + +/** + * test verify + * + * @return void + **/ + function testVerify() { + $this->Task->startup(); + $this->Task->args = array('Aco'); + $this->Task->Acl->Aco = $this->getMock('Aco', array('verify')); + $this->Task->Acl->Aco->expects($this->once()) + ->method('verify') + ->will($this->returnValue(true)); + + $this->Task->expects($this->once()) + ->method('out') + ->with($this->matchesRegularExpression('/valid/')); + + $this->Task->verify(); + } + +/** + * test startup + * + * @return void + **/ + function testStartup() { + $this->assertEqual($this->Task->Acl, null); + $this->Task->startup(); + $this->assertInstanceOf('AclComponent', $this->Task->Acl); + } + +/** + * clean fixtures and setup mock + * + * @return void + **/ + function _cleanAndSetup() { + $tableName = $this->db->fullTableName('acos'); + $this->db->execute('DELETE FROM ' . $tableName); + $this->Task->expects($this->any()) + ->method('getControllerList') + ->will($this->returnValue(array('CommentsController', 'PostsController', 'BigLongNamesController'))); + + $this->Task->startup(); + } +/** + * Test aco_update method. + * + * @return void + **/ + function testAcoUpdate() { + $this->_cleanAndSetup(); + $this->Task->aco_update(); + + $Aco = $this->Task->Acl->Aco; + + $result = $Aco->node('controllers/Comments'); + $this->assertEqual($result[0]['Aco']['alias'], 'Comments'); + + $result = $Aco->children($result[0]['Aco']['id']); + $this->assertEqual(count($result), 3); + $this->assertEqual($result[0]['Aco']['alias'], 'add'); + $this->assertEqual($result[1]['Aco']['alias'], 'index'); + $this->assertEqual($result[2]['Aco']['alias'], 'delete'); + + $result = $Aco->node('controllers/Posts'); + $this->assertEqual($result[0]['Aco']['alias'], 'Posts'); + $result = $Aco->children($result[0]['Aco']['id']); + $this->assertEqual(count($result), 3); + + $result = $Aco->node('controllers/BigLongNames'); + $this->assertEqual($result[0]['Aco']['alias'], 'BigLongNames'); + $result = $Aco->children($result[0]['Aco']['id']); + $this->assertEqual(count($result), 4); + } + +/** + * test syncing of Aco records + * + * @return void + **/ + function testAcoSyncRemoveMethods() { + $this->_cleanAndSetup(); + $this->Task->aco_update(); + + $Aco = $this->Task->Acl->Aco; + $Aco->cacheQueries = false; + + $result = $Aco->node('controllers/Comments'); + $new = array( + 'parent_id' => $result[0]['Aco']['id'], + 'alias' => 'some_method' + ); + $Aco->create($new); + $Aco->save(); + $children = $Aco->children($result[0]['Aco']['id']); + $this->assertEqual(count($children), 4); + + $this->Task->aco_sync(); + $children = $Aco->children($result[0]['Aco']['id']); + $this->assertEqual(count($children), 3); + + $method = $Aco->node('controllers/Commments/some_method'); + $this->assertFalse($method); + } + +/** + * test adding methods with aco_update + * + * @return void + **/ + function testAcoUpdateAddingMethods() { + $this->_cleanAndSetup(); + $this->Task->aco_update(); + + $Aco = $this->Task->Acl->Aco; + $Aco->cacheQueries = false; + + $result = $Aco->node('controllers/Comments'); + $children = $Aco->children($result[0]['Aco']['id']); + $this->assertEqual(count($children), 3); + + $Aco->delete($children[0]['Aco']['id']); + $Aco->delete($children[1]['Aco']['id']); + $this->Task->aco_update(); + + $children = $Aco->children($result[0]['Aco']['id']); + $this->assertEqual(count($children), 3); + } + +/** + * test adding controllers on sync + * + * @return void + **/ + function testAddingControllers() { + $this->_cleanAndSetup(); + $this->Task->aco_update(); + + $Aco = $this->Task->Acl->Aco; + $Aco->cacheQueries = false; + + $result = $Aco->node('controllers/Comments'); + $Aco->delete($result[0]['Aco']['id']); + + $this->Task->aco_update(); + $newResult = $Aco->node('controllers/Comments'); + $this->assertNotEqual($newResult[0]['Aco']['id'], $result[0]['Aco']['id']); + } +} diff --git a/app/Plugin/AclExtras/Test/test_controllers.php b/app/Plugin/AclExtras/Test/test_controllers.php new file mode 100755 index 000000000..714e7616a --- /dev/null +++ b/app/Plugin/AclExtras/Test/test_controllers.php @@ -0,0 +1,53 @@ +isWindows()) { + $default_facility = LOG_USER; + } else { + $default_facility= LOG_LOCAL0; + } + $options += array('ident' => LOGS, 'facility' => $default_facility); + $this->_ident = $options['ident']; + $this->_facility = $options['facility']; + } + +/** +* Utilty method to identify if we're running on a Windows box. +* +* @return boolean if running on windows. +*/ + function isWindows() { + return (DIRECTORY_SEPARATOR == '\\' ? true : false); + } + +/** +* Implements writing to the specified syslog +* +* @param string $type The type of log you are making. +* @param string $message The message you want to log. +* @return boolean success of write. +*/ + function write($type, $message) { + $debugTypes = array('notice', 'info', 'debug'); + $priority = LOG_INFO; + if ($type == 'error' || $type == 'warning') { + $priority = LOG_ERR; + } elseif (in_array($type, $debugTypes)) { + $priority = LOG_DEBUG; + } + $output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n"; + if (!openlog($this->_ident, LOG_PID | LOG_PERROR, $this->_facility)) { + return false; + } + $result = syslog($priority, $output); + closelog(); + return $result; + } +} +?> \ No newline at end of file diff --git a/app/README.txt b/app/README.txt index e17eac6c5..bc41f8ca8 100755 --- a/app/README.txt +++ b/app/README.txt @@ -1,14 +1,25 @@ - -TODOs +TODOs v0.2.2 to v0.2.3 ----- +DB Update +- UpdateShell with in/out + Auth - Prevent bruteforce auth attempts -implement auditing/logging system -- add / edit events and signatures -- failed / success logins (with source IP, headers,...) +Acl +- inactive buttons + - must be non-clickable. + - JavaScript include. + - DOM read and disable button_offXX. +- clean-up to first cut. + - saveAcl, from GroupsController to AppController and inherit to *Controllers. + +auditing/logging system +- logins + - add source IP (headers,...); + - failed logins. Security - force cookie reset after login @@ -18,7 +29,7 @@ INSTALLATION INSTRUCTIONS ------------------------- Install the following libraries: apt-get install zip -apt-get install pear +apt-get install php-pear pear install Crypt_GPG # need version >1.3.0 TODO rewrite instructions using git clones and git submodules @@ -77,6 +88,17 @@ Don't forget to change the email, password and authentication key after installa +UPDATE INSTRUCTIONS +------------------- + +To be sure, dump your database before updating. + +CyDefSIG from 0.2.2 to 0.2.3 needs a database migration and population. +This is done executing /var/www/cydefsig/app/Console/shell/migrate-0.2.2-0.2.3.sh +and answer (y)es to all the questions asked. + + + Recommended patches ------------------- -By default CakePHP exposes his name and version in email headers. Apply a patch to remove this behavior. \ No newline at end of file +By default CakePHP exposes his name and version in email headers. Apply a patch to remove this behavior. diff --git a/app/README.ubuntu.txt b/app/README.ubuntu.txt new file mode 100755 index 000000000..c7b3bef46 --- /dev/null +++ b/app/README.ubuntu.txt @@ -0,0 +1,34 @@ +INSTALLATION INSTRUCTIONS +------------------------- +If on Ubuntu, besides the DocumentRoot, +you have to change the AllowOverride from None to All as well. + + DocumentRoot /var/www/cydefsig/app/webroot/ + + Options FollowSymLinks + AllowOverride All + + + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Order allow,deny + allow from all + + +Find the original below, for reference. + + DocumentRoot /var/www + + + Options FollowSymLinks + AllowOverride None + + + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + allow from all + + +Now /etc/init.d/apache2 restart +and you are done, and now able to use the application. \ No newline at end of file diff --git a/app/View/Attributes/edit.ctp b/app/View/Attributes/edit.ctp index 448c6fe22..192fdefbf 100755 --- a/app/View/Attributes/edit.ctp +++ b/app/View/Attributes/edit.ctp @@ -1,3 +1,7 @@ +
Form->create('Attribute');?>
@@ -38,7 +42,10 @@
@@ -112,3 +119,10 @@ $('#AttributeType').val(type_value); Js->writeBuffer(); // Write cached scripts ?> + + + \ No newline at end of file diff --git a/app/View/Attributes/index.ctp b/app/View/Attributes/index.ctp index 0605c17ad..74a4586d4 100755 --- a/app/View/Attributes/index.ctp +++ b/app/View/Attributes/index.ctp @@ -1,3 +1,7 @@ +

@@ -35,8 +39,9 @@  
Html->link(__('Edit'), array('action' => 'edit', $attribute['Attribute']['id'])); - echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $attribute['Attribute']['id']), null, __('Are you sure you want to delete this attribute?')); + echo $this->Html->link(__('Edit'), array('action' => 'edit', $attribute['Attribute']['id']), $isAclModify||($attribute['Event']['user_id'] == $me['id']) ? null:array('id' => $button_modify_status.$buttonCounter++, 'class' => $button_modify_status)); + if ($isAclModify || $attribute['Event']['user_id'] == $me['id']) echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $attribute['Attribute']['id']), null, __('Are you sure you want to delete this attribute?')); + else echo $this->Html->link(__('Delete'), array('action' => 'delete', $attribute['Attribute']['id']), array('id' => $button_modify_status.$buttonCounter++,'class' => $button_modify_status)); } echo $this->Html->link(__('View'), array('controller' => 'events', 'action' => 'view', $attribute['Attribute']['event_id'])); ?> @@ -64,3 +69,400 @@ element('actions_menu'); ?> + + + \ No newline at end of file diff --git a/app/View/Elements/actions_menu.ctp b/app/View/Elements/actions_menu.ctp index 64b4dbf7b..04ea9f096 100755 --- a/app/View/Elements/actions_menu.ctp +++ b/app/View/Elements/actions_menu.ctp @@ -1,4 +1,5 @@ -
  • Html->link(__('New Event', true), array('controller' => 'events', 'action' => 'add')); ?>
  • + +
  • Html->link(__('New Event', true), array('controller' => 'events', 'action' => 'add'), array('id' => $button_add_status,'class' => $button_add_status,'disabled'=>'disabled','readonly'=>'readonly')); ?>
  • Html->link(__('List Events', true), array('controller' => 'events', 'action' => 'index')); ?>
  • Html->link(__('List Attributes', true), array('controller' => 'attributes', 'action' => 'index')); ?>
  • Html->link(__('Search Attributes', true), array('controller' => 'attributes', 'action' => 'search')); ?>
  • @@ -23,4 +24,10 @@

  • Html->link(__('New User', true), array('controller' => 'users', 'action' => 'add', 'admin' => true)); ?>
  • Html->link(__('List Users', true), array('controller' => 'users', 'action' => 'index', 'admin' => true)); ?>
  • +
  • Html->link(__('New Group', true), array('controller' => 'groups', 'action' => 'add', 'admin' => true)); ?>
  • +
  • Html->link(__('List Groups', true), array('controller' => 'groups', 'action' => 'index', 'admin' => true)); ?>
  • +
  •  
  • +

    +
  • Html->link(__('List Logs', true), array('controller' => 'logs', 'action' => 'index', 'admin' => true)); ?>
  • +
  • Html->link(__('Search Logs', true), array('controller' => 'logs', 'action' => 'admin_search', 'admin' => true,'disabled'=>'disabled','readonly'=>'readonly')); ?>
  • \ No newline at end of file diff --git a/app/View/Events/index.ctp b/app/View/Events/index.ctp index ac354afbf..177c82558 100755 --- a/app/View/Events/index.ctp +++ b/app/View/Events/index.ctp @@ -1,3 +1,9 @@ +

    Events

    @@ -38,13 +44,15 @@ @@ -154,8 +170,8 @@
      -
    • Html->link('Add Attribute', array('controller' => 'attributes', 'action' => 'add', $event['Event']['id']));?>
    • -
    • Html->link('Add Attachment', array('controller' => 'attributes', 'action' => 'add_attachment', $event['Event']['id']));?>
    • +
    • Html->link('Add Attribute', array('controller' => 'attributes', 'action' => 'add', $event['Event']['id']), array('id' =>$button_add_status.$buttonCounter++,'class' => $button_add_status));?>
    • +
    • Html->link('Add Attachment', array('controller' => 'attributes', 'action' => 'add_attachment', $event['Event']['id']),array('id' =>$button_add_status.$buttonCounter++,'class' => $button_add_status));?>
    @@ -166,13 +182,143 @@
      -
    • Html->link(__('Add Attribute', true), array('controller' => 'attributes', 'action' => 'add', $event['Event']['id']));?>
    • -
    • Html->link(__('Add Attachment', true), array('controller' => 'attributes', 'action' => 'add_attachment', $event['Event']['id']));?>
    • -
    • Html->link(__('Edit Event', true), array('action' => 'edit', $event['Event']['id'])); ?>
    • -
    • Form->postLink(__('Delete Event'), array('action' => 'delete', $event['Event']['id']), null, __('Are you sure you want to delete # %s?', $event['Event']['id'])); ?>
    • +
    • Html->link(__('Add Attribute', true), array('controller' => 'attributes', 'action' => 'add', $event['Event']['id']), array('id' =>$button_add_status.$buttonCounter++,'class' => $button_add_status));?>
    • +
    • Html->link(__('Add Attachment', true), array('controller' => 'attributes', 'action' => 'add_attachment', $event['Event']['id']), array('id' =>$button_add_status.$buttonCounter++,'class' => $button_add_status));?>
    • +
    • Html->link(__('Edit Event', true), array('action' => 'edit', $event['Event']['id']), array('id' =>$button_modify_status.$buttonCounter++,'class' => $button_modify_status)); ?>
    • +
    • Form->postLink(__('Delete Event'), array('action' => 'delete', $event['Event']['id']), null, __('Are you sure you want to delete # %s?', $event['Event']['id'])); + else echo $this->Html->link(__('Delete Event'), array('action' => 'delete', $event['Event']['id']), array('id' =>$button_modify_status.$buttonCounter++,'class' => $button_modify_status)); + ?>
    •  
    • element('actions_menu'); ?>
    + + + \ No newline at end of file diff --git a/app/View/Groups/add.ctp b/app/View/Groups/add.ctp new file mode 100755 index 000000000..1e37e2b4f --- /dev/null +++ b/app/View/Groups/add.ctp @@ -0,0 +1,19 @@ +
    +Form->create('Group');?> +
    + + Form->input('name'); + ?> +
    +Form->end(__('Submit'));?> +
    +
    +

    +
      + +
    • Html->link(__('List Groups'), array('action' => 'index'));?>
    • +
    • Html->link(__('List Users'), array('controller' => 'users', 'action' => 'index')); ?>
    • +
    • Html->link(__('New User'), array('controller' => 'users', 'action' => 'add')); ?>
    • +
    +
    diff --git a/app/View/Groups/admin_add.ctp b/app/View/Groups/admin_add.ctp new file mode 100755 index 000000000..c1d0972e1 --- /dev/null +++ b/app/View/Groups/admin_add.ctp @@ -0,0 +1,19 @@ +
    +Form->create('Group');?> +
    + + Form->input('name'); + echo $this->Form->input('perm_add'); + echo $this->Form->input('perm_modify'); + echo $this->Form->input('perm_publish'); + echo $this->Form->input('perm_full'); + ?> +
    +Form->end(__('Submit'));?> +
    +
    +
      + element('actions_menu'); ?> +
    +
    diff --git a/app/View/Groups/admin_edit.ctp b/app/View/Groups/admin_edit.ctp new file mode 100755 index 000000000..4d813b880 --- /dev/null +++ b/app/View/Groups/admin_edit.ctp @@ -0,0 +1,23 @@ +
    +Form->create('Group');?> +
    + + Form->input('name');?> +
    + + Form->input('perm_add', array( 'label' => 'add')); + echo $this->Form->input('perm_modify', array( 'label' => 'modify')); + echo $this->Form->input('perm_publish', array( 'label' => 'publish')); + echo $this->Form->input('perm_full', array( 'label' => 'full')); + ?> +
    +
    +Form->end(__('Submit'));?> +
    +
    +
      + element('actions_menu'); ?> +
    +
    diff --git a/app/View/Groups/admin_index.ctp b/app/View/Groups/admin_index.ctp new file mode 100755 index 000000000..169b227da --- /dev/null +++ b/app/View/Groups/admin_index.ctp @@ -0,0 +1,55 @@ +
    +

    +
    Form->postLink('Publish Event', array('action' => 'alert', $event['Event']['id']), null, 'Are you sure this event is complete and everyone should be informed?'); + if ($isAclPublish || $event['Event']['user_id'] == $me['id']) echo $this->Form->postLink('Publish Event', array('action' => 'alert', $event['Event']['id']), array('action' => 'alert', $event['Event']['id']), 'Are you sure this event is complete and everyone should be informed?'); + else echo $this->Html->link('Publish Event', array('id' =>$button_publish_status.$buttonCounter++,'class' => $button_publish_status, 'action' => 'alert', $event['Event']['id']), array('id' =>$button_publish_status.$buttonCounter++,'class' => $button_publish_status, 'action' => 'alert', $event['Event']['id'])); elseif (0 == $event['Event']['published']) echo 'Not published'; ?> Html->link(__('Edit', true), array('action' => 'edit', $event['Event']['id'])); - echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $event['Event']['id']), null, __('Are you sure you want to delete # %s?', $event['Event']['id'])); + echo $this->Html->link(__('Edit', true), array('action' => 'edit', $event['Event']['id']), $isAclModify||($event['Event']['user_id'] == $me['id']) ? null:array('id' => $button_modify_status.$buttonCounter++, 'class' => $button_modify_status)); + if ($isAclModify || $event['Event']['user_id'] == $me['id']) echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $event['Event']['id']), null, __('Are you sure you want to delete # %s?', $event['Event']['id'])); + else echo $this->Html->link(__('Delete'), array('action' => 'delete', $event['Event']['id']), array('id' =>$button_modify_status.$buttonCounter++,'class' => $button_modify_status)); } ?> Html->link(__('View', true), array('action' => 'view', $event['Event']['id'])); ?> @@ -71,3 +79,580 @@ element('actions_menu'); ?> + + + \ No newline at end of file diff --git a/app/View/Events/view.ctp b/app/View/Events/view.ctp index 101a7243b..644cd3a54 100755 --- a/app/View/Events/view.ctp +++ b/app/View/Events/view.ctp @@ -1,11 +1,22 @@ +
    • Form->postLink('Publish Event', array('action' => 'alert', $event['Event']['id']), null, 'Are you sure this event is complete and everyone should be informed?'); - echo $this->Form->postLink('Publish (no email)', array('action' => 'publish', $event['Event']['id']), null, 'Publish but do NOT send alert email? Only for minor changes!'); + if ($may_publish) { + echo $this->Form->postLink('Publish Event', array('action' => 'alert', $event['Event']['id']), null, 'Are you sure this event is complete and everyone should be informed?'); + echo $this->Form->postLink('Publish (no email)', array('action' => 'publish', $event['Event']['id']), null, 'Publish but do NOT send alert email? Only for minor changes!'); + } else { + echo $this->Html->link('Publish Event', array('action' => 'alert', $event['Event']['id']), array('id' => $button_publish_status.$buttonCounter++, 'class' => $button_publish_status)); + echo $this->Html->link('Publish (no email)', array('action' => 'publish', $event['Event']['id']), array('id' => $button_publish_status.$buttonCounter++, 'class' => $button_publish_status)); + } ?>
    • Not published
    @@ -141,8 +152,13 @@
    Html->link(__('Edit', true), array('controller' => 'attributes', 'action' => 'edit', $attribute['id'])); - echo $this->Form->postLink(__('Delete'), array('controller' => 'attributes', 'action' => 'delete', $attribute['id']), null, __('Are you sure you want to delete this attribute?')); + if ($isAclModify) { + echo $this->Html->link(__('Edit', true), array('controller' => 'attributes', 'action' => 'edit', $attribute['id'])); + echo $this->Form->postLink(__('Delete'), array('controller' => 'attributes', 'action' => 'delete', $attribute['id']), null, __('Are you sure you want to delete this attribute?')); + } else { + echo $this->Html->link(__('Edit', true), array('controller' => 'attributes', 'action' => 'edit', $attribute['id']), array('id' =>$button_modify_status.$buttonCounter++,'class' => $button_modify_status)); + echo $this->Html->link(__('Delete'), array('controller' => 'attributes', 'action' => 'delete', $attribute['id']), array('id' =>$button_modify_status.$buttonCounter++,'class' => $button_modify_status)); + } ?>
    + + + + + + + + + + + + + + + + + + + + +
    Paginator->sort('id');?>Paginator->sort('name');?>Paginator->sort('add');?>Paginator->sort('modify');?>Paginator->sort('publish');?>Paginator->sort('full');?>
    +   +   +   +   +   +   + Html->link(__('View'), array('admin' => true, 'action' => 'view', $group['Group']['id'])); ?> + Html->link(__('Edit'), array('admin' => true, 'action' => 'edit', $group['Group']['id'])); ?> + Form->postLink(__('Delete'), array('admin' => true, 'action' => 'delete', $group['Group']['id']), null, __('Are you sure you want to delete # %s?', $group['Group']['id'])); ?> +
    +

    + Paginator->counter(array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + )); + ?>

    + +
    + Paginator->prev('< ' . __('previous'), array(), null, array('class' => 'prev disabled')); + echo $this->Paginator->numbers(array('separator' => '')); + echo $this->Paginator->next(__('next') . ' >', array(), null, array('class' => 'next disabled')); + ?> +
    +
    +
    +
      + element('actions_menu'); ?> +
    +
    diff --git a/app/View/Groups/admin_view.ctp b/app/View/Groups/admin_view.ctp new file mode 100755 index 000000000..6e4c0bef8 --- /dev/null +++ b/app/View/Groups/admin_view.ctp @@ -0,0 +1,47 @@ +
    + +

    +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    +
    +

    +
      +
    • Html->link(__('Edit Group'), array('admin' => true, 'action' => 'edit', $group['Group']['id'])); ?>
    • +
    • Form->postLink(__('Delete Group'), array('admin' => true, 'action' => 'delete', $group['Group']['id']), null, __('Are you sure you want to delete # %s?', $group['Group']['id'])); ?>
    • +
    • Html->link(__('List Groups'), array('admin' => true, 'action' => 'index')); ?>
    • +
    • Html->link(__('New Group'), array('admin' => true, 'action' => 'add')); ?>
    • +
    +
    \ No newline at end of file diff --git a/app/View/Groups/edit.ctp b/app/View/Groups/edit.ctp new file mode 100755 index 000000000..75337df81 --- /dev/null +++ b/app/View/Groups/edit.ctp @@ -0,0 +1,21 @@ +
    +Form->create('Group');?> +
    + + Form->input('id'); + echo $this->Form->input('name'); + ?> +
    +Form->end(__('Submit'));?> +
    +
    +

    +
      + +
    • Form->postLink(__('Delete'), array('action' => 'delete', $this->Form->value('Group.id')), null, __('Are you sure you want to delete # %s?', $this->Form->value('Group.id'))); ?>
    • +
    • Html->link(__('List Groups'), array('action' => 'index'));?>
    • +
    • Html->link(__('List Users'), array('controller' => 'users', 'action' => 'index')); ?>
    • +
    • Html->link(__('New User'), array('controller' => 'users', 'action' => 'add')); ?>
    • +
    +
    diff --git a/app/View/Groups/index.ctp b/app/View/Groups/index.ctp new file mode 100755 index 000000000..ed37eddde --- /dev/null +++ b/app/View/Groups/index.ctp @@ -0,0 +1,48 @@ +
    +

    + + + + + + + + + + + + + + + + + +
    Paginator->sort('id');?>Paginator->sort('name');?>Paginator->sort('created');?>Paginator->sort('modified');?>
         + Html->link(__('View'), array('action' => 'view', $group['Group']['id'])); ?> + Html->link(__('Edit'), array('action' => 'edit', $group['Group']['id'])); ?> + Form->postLink(__('Delete'), array('action' => 'delete', $group['Group']['id']), null, __('Are you sure you want to delete # %s?', $group['Group']['id'])); ?> +
    +

    + Paginator->counter(array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + )); + ?>

    + +
    + Paginator->prev('< ' . __('previous'), array(), null, array('class' => 'prev disabled')); + echo $this->Paginator->numbers(array('separator' => '')); + echo $this->Paginator->next(__('next') . ' >', array(), null, array('class' => 'next disabled')); + ?> +
    +
    +
    +

    +
      +
    • Html->link(__('New Group'), array('action' => 'add')); ?>
    • +
    • Html->link(__('List Users'), array('controller' => 'users', 'action' => 'index')); ?>
    • +
    • Html->link(__('New User'), array('controller' => 'users', 'action' => 'add')); ?>
    • +
    +
    diff --git a/app/View/Groups/view.ctp b/app/View/Groups/view.ctp new file mode 100755 index 000000000..7d3234f3a --- /dev/null +++ b/app/View/Groups/view.ctp @@ -0,0 +1,40 @@ +
    +

    +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    +
    +
      + element('actions_menu'); ?> +
    +
    \ No newline at end of file diff --git a/app/View/Layouts/default.ctp b/app/View/Layouts/default.ctp old mode 100644 new mode 100755 index 9113e06ef..9590c4df0 --- a/app/View/Layouts/default.ctp +++ b/app/View/Layouts/default.ctp @@ -37,6 +37,8 @@ echo $this->Html->script('jquery-1.7.2.min'); // Include jQuery library ?> + +
    diff --git a/app/View/Logs/admin_index.ctp b/app/View/Logs/admin_index.ctp new file mode 100755 index 000000000..e7848b349 --- /dev/null +++ b/app/View/Logs/admin_index.ctp @@ -0,0 +1,59 @@ +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Paginator->sort('id');?>Paginator->sort('email');?>Paginator->sort('org');?>Paginator->sort('created');?>Paginator->sort('action');?>Paginator->sort('title');?>Paginator->sort('change');?>
    +   +   +   +   +   +   +   + Html->link(__('View'), array('admin' => true, 'action' => 'view', $log['Log']['id'])); ?> +
    +

    + Paginator->counter(array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + )); + ?>

    + +
    + Paginator->prev('< ' . __('previous'), array(), null, array('class' => 'prev disabled')); + echo $this->Paginator->numbers(array('separator' => '')); + echo $this->Paginator->next(__('next') . ' >', array(), null, array('class' => 'next disabled')); + ?> +
    +
    +
    +
      + element('actions_menu'); ?> +
    +
    diff --git a/app/View/Logs/admin_search.ctp b/app/View/Logs/admin_search.ctp new file mode 100755 index 000000000..d7cb089d3 --- /dev/null +++ b/app/View/Logs/admin_search.ctp @@ -0,0 +1,53 @@ +
    +Form->create('Log');?> +
    + + Form->input('email', array( 'label' => 'Email')); + echo $this->Form->input('org', array( 'label' => 'Org')); + echo $this->Form->input('action', array('between' => $this->Html->div('forminfo', '', array('id'=> 'LogActionDiv')))); + + echo $this->Form->input('title', array( 'label' => 'Title')); + echo $this->Form->input('change', array( 'label' => 'Change')); + ?> +
    +Form->end(__('Search', true));?> +
    +
    +
      + element('actions_menu'); ?> +
    +
    + +Js->writeBuffer(); // Write cached scripts ?> diff --git a/app/View/Logs/admin_view.ctp b/app/View/Logs/admin_view.ctp new file mode 100755 index 000000000..5654cb4f3 --- /dev/null +++ b/app/View/Logs/admin_view.ctp @@ -0,0 +1,54 @@ +
    + +

    +
    +
    +
    + +   +
    + +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    + +   +
    +
    +
    +
    +

    +
      +
    • Html->link(__('List Logs'), array('admin' => true, 'action' => 'index')); ?>
    • +
    +
    \ No newline at end of file diff --git a/app/View/Logs/index.ctp b/app/View/Logs/index.ctp new file mode 100755 index 000000000..e7848b349 --- /dev/null +++ b/app/View/Logs/index.ctp @@ -0,0 +1,59 @@ +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Paginator->sort('id');?>Paginator->sort('email');?>Paginator->sort('org');?>Paginator->sort('created');?>Paginator->sort('action');?>Paginator->sort('title');?>Paginator->sort('change');?>
    +   +   +   +   +   +   +   + Html->link(__('View'), array('admin' => true, 'action' => 'view', $log['Log']['id'])); ?> +
    +

    + Paginator->counter(array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + )); + ?>

    + +
    + Paginator->prev('< ' . __('previous'), array(), null, array('class' => 'prev disabled')); + echo $this->Paginator->numbers(array('separator' => '')); + echo $this->Paginator->next(__('next') . ' >', array(), null, array('class' => 'next disabled')); + ?> +
    +
    +
    +
      + element('actions_menu'); ?> +
    +
    diff --git a/app/View/Servers/index.ctp b/app/View/Servers/index.ctp old mode 100644 new mode 100755 index ec1047e7f..ba2c0a88c --- a/app/View/Servers/index.ctp +++ b/app/View/Servers/index.ctp @@ -1,3 +1,8 @@ +

    @@ -24,8 +29,9 @@
    - Html->link(__('Edit'), array('action' => 'edit', $server['Server']['id'])); ?> - Form->postLink(__('Delete'), array('action' => 'delete', $server['Server']['id']), null, __('Are you sure you want to delete # %s?', $server['Server']['id'])); ?> + Html->link(__('Edit'), array('action' => 'edit', $server['Server']['id']), $isAclModify||($server['Server']['org'] == $me['org']) ? null:array('id' => $button_modify_status.$buttonCounter++, 'class' => $button_modify_status)); ?> + Form->postLink(__('Delete'), array('action' => 'delete', $server['Server']['id']), null, __('Are you sure you want to delete # %s?', $server['Server']['id'])); + else echo $this->Html->link(__('Delete'), array('action' => 'delete', $server['Server']['id']), array('id' => $button_modify_status.$buttonCounter++,'class' => $button_modify_status)); ?> Form->postLink(__('Pull'), array('action' => 'pull', $server['Server']['id']) ); ?> Form->postLink(__('Push'), array('action' => 'push', $server['Server']['id']) ); ?> @@ -54,9 +60,404 @@
      -
    • Html->link(__('New Server'), array('controller' => 'servers', 'action' => 'add')); ?>
    • +
    • Html->link(__('New Server'), array('controller' => 'servers', 'action' => 'add'), array('id' =>$button_add_status.$buttonCounter++,'class' => $button_add_status)); ?>
    • Html->link(__('List Servers'), array('controller' => 'servers', 'action' => 'index'));?>
    •  
    • element('actions_menu'); ?>
    + \ No newline at end of file diff --git a/app/View/Users/admin_add.ctp b/app/View/Users/admin_add.ctp old mode 100644 new mode 100755 index 15da789a0..51222612b --- a/app/View/Users/admin_add.ctp +++ b/app/View/Users/admin_add.ctp @@ -7,6 +7,7 @@ echo $this->Form->input('password'); echo $this->Form->input('confirm_password', array('type' => 'password', 'div' => array('class' => 'input password required'))); echo $this->Form->input('org'); + echo $this->Form->input('group_id'); echo $this->Form->input('autoalert'); echo $this->Form->input('authkey', array('value' => $authkey)); echo $this->Form->input('nids_sid'); diff --git a/app/View/Users/admin_edit.ctp b/app/View/Users/admin_edit.ctp old mode 100644 new mode 100755 index 9eb887606..c7240b6b2 --- a/app/View/Users/admin_edit.ctp +++ b/app/View/Users/admin_edit.ctp @@ -7,6 +7,7 @@ echo $this->Form->input('password'); echo $this->Form->input('confirm_password', array('type' => 'password', 'div' => array('class' => 'input password required'))); echo $this->Form->input('org'); + echo $this->Form->input('group_id'); // TODO ACL, User edit group_id. echo $this->Form->input('autoalert'); echo $this->Form->input('authkey'); echo $this->Form->input('nids_sid'); diff --git a/app/View/Users/admin_index.ctp b/app/View/Users/admin_index.ctp old mode 100644 new mode 100755 index 4df624e1b..5771a12d8 --- a/app/View/Users/admin_index.ctp +++ b/app/View/Users/admin_index.ctp @@ -1,9 +1,16 @@ +

    + @@ -20,6 +27,8 @@ + @@ -33,8 +42,10 @@   @@ -59,3 +70,398 @@ element('actions_menu'); ?> + \ No newline at end of file diff --git a/app/View/Users/admin_view.ctp b/app/View/Users/admin_view.ctp old mode 100644 new mode 100755 index 13316c211..ce4329aa7 --- a/app/View/Users/admin_view.ctp +++ b/app/View/Users/admin_view.ctp @@ -1,6 +1,11 @@ +
    -
    • Html->link(__('Edit Profile', true), array('admin' => true, 'action' => 'edit', $user['User']['id'])); ?>
    +
    • Html->link(__('Edit Profile', true), array('admin' => true, 'action' => 'edit', $user['User']['id']), array('id' => $button_modify_status.$buttonCounter++,'class' => $button_modify_status)); ?>

    @@ -19,6 +24,11 @@   +
    +
    + Html->link($user['Group']['name'], array('controller' => 'groups', 'action' => 'view', $user['Group']['id'])); ?> +   +
    @@ -66,12 +76,15 @@

      -
    • Html->link(__('Edit User'), array('admin' => true, 'action' => 'edit', $user['User']['id'])); ?>
    • -
    • Form->postLink(__('Delete User'), array('admin' => true, 'action' => 'delete', $user['User']['id']), null, __('Are you sure you want to delete # %s?', $user['User']['id'])); ?>
    • +
    • Html->link(__('Edit User'), array('admin' => true, 'action' => 'edit', $user['User']['id']), array('id' => $button_modify_status.$buttonCounter++,'class' => $button_modify_status)); ?>
    • +
    • Form->postLink(__('Delete User'), array('admin' => true, 'action' => 'delete', $user['User']['id']), null, __('Are you sure you want to delete # %s?', $user['User']['id'])); + else echo $this->Html->link(__('Delete User'), array('admin' => true, 'action' => 'delete', $user['User']['id']), array('id' => $button_modify_status.$buttonCounter++,'class' => $button_modify_status)); + ?>
    • Html->link(__('List Users'), array('admin' => true, 'action' => 'index')); ?>
    • -
    • Html->link(__('New User'), array('admin' => true, 'action' => 'add')); ?>
    • +
    • Html->link(__('New User'), array('admin' => true, 'action' => 'add'), array('id' => $button_add_status,'class' => $button_add_status,'disabled'=>'disabled','readonly'=>'readonly')); ?>
    • Html->link(__('List Events'), array('controller' => 'events', 'action' => 'index')); ?>
    • -
    • Html->link(__('New Event'), array('controller' => 'events', 'action' => 'add')); ?>
    • +
    • Html->link(__('New Event'), array('controller' => 'events', 'action' => 'add'), array('id' => $button_add_status,'class' => $button_add_status,'disabled'=>'disabled','readonly'=>'readonly')); ?>
    diff --git a/app/View/Users/edit.ctp b/app/View/Users/edit.ctp index 469550f3d..09a6c7312 100755 --- a/app/View/Users/edit.ctp +++ b/app/View/Users/edit.ctp @@ -8,6 +8,7 @@ echo $this->Form->input('confirm_password', array('type' => 'password', 'div' => array('class' => 'input password required'))); if ($isAdmin) echo $this->Form->input('org'); else echo $this->Form->input('org', array('disabled' => 'disabled')); + echo $this->Form->input('group_id', array('disabled' => 'disabled')); // TODO ACL, check, My Profile not edit group_id. echo $this->Form->input('autoalert'); echo $this->Form->input('nids_sid'); echo $this->Form->input('gpgkey'); diff --git a/app/View/Users/view.ctp b/app/View/Users/view.ctp index b22fbc931..9cbd17f00 100755 --- a/app/View/Users/view.ctp +++ b/app/View/Users/view.ctp @@ -19,6 +19,11 @@   +
    +
    + Html->link($user['Group']['name'], array('controller' => 'groups', 'action' => 'view', $user['Group']['id'])); ?> +   +
    diff --git a/app/technical_design/TD-ACL.txt b/app/technical_design/TD-ACL.txt new file mode 100755 index 000000000..1b82f0358 --- /dev/null +++ b/app/technical_design/TD-ACL.txt @@ -0,0 +1,16 @@ +ACL Technical Design (TD) + +To use Access Control in CakePHP we use itś own AclComponent. + +http://book.cakephp.org/2.0/en/tutorials-and-examples/simple-acl-controlled-application/simple-acl-controlled-application.html +http://book.cakephp.org/2.0/en/tutorials-and-examples/simple-acl-controlled-application/part-two.html + + CakePHP example, just telling how to populate and connect the CakePHP AclController. + +http://stackoverflow.com/questions/6154285/aros-table-in-cakephp-is-still-including-users-even-after-bindnode + + If ACL on Group level add this small correction, to just only add Groups to CakePHP ACL tables and not to add the Users. + +http://bakery.cakephp.org/articles/theshz/2006/11/28/user-permissions-and-cakephp-acl + + Calling the ACL from within a controller. diff --git a/app/technical_design/TD-Audit.txt b/app/technical_design/TD-Audit.txt new file mode 100755 index 000000000..c4dd3e108 --- /dev/null +++ b/app/technical_design/TD-Audit.txt @@ -0,0 +1,28 @@ +Audit Technical Design (TD) + +To log in CakePHP we use an existing Model Behavior, +to write to a log database table. + +https://github.com/eskil-saatvedt/CakePHP-Assets/blob/master/models/behaviors/LogableBehavior.php + + Adds the logable Model Behavior. + +http://bakery.cakephp.org/articles/rikdc/2010/06/07/syslog-component + + Ads the syslog capability. + +http://bakery.cakephp.org/articles/alkemann/2008/10/21/logablebehavior +http://www.bitsntricks.com/cakephp-logable-behaviour/ + + Short explaination itś use. + +http://stackoverflow.com/questions/9791633/check-if-cakephp-update-changes-a-variable-in-update + + This Logable Model Behavior seemed not to work in the UsersController, +so the change checks are done manual coded in the UsersController. + + + +https://github.com/joebeeson/referee#readme + + Can be handy lateron for log to db or syslog? \ No newline at end of file diff --git a/app/technical_design/TD-forum.txt b/app/technical_design/TD-forum.txt new file mode 100755 index 000000000..993b44a26 --- /dev/null +++ b/app/technical_design/TD-forum.txt @@ -0,0 +1,12 @@ +Forum Technical Design (TD) + +We use a plugin giving Forum use in CakePHP. + +http://milesj.me/code/cakephp/forum + +Alternative PhpBB in conjunction with CakePHP Users and Groups tables. + +http://bakery.cakephp.org/articles/wilsonsheldon/2009/01/13/phpbb3-api-bridge +http://www.phpbb.com/community/viewtopic.php?f=71&t=993475 + + CakePHP and a PhpBB3 forum. \ No newline at end of file diff --git a/app/webroot/css/cake.generic.css b/app/webroot/css/cake.generic.css old mode 100644 new mode 100755 index 5901e6394..888497199 --- a/app/webroot/css/cake.generic.css +++ b/app/webroot/css/cake.generic.css @@ -772,3 +772,11 @@ pre { #url-rewriting-warning { display:none; } + +/* to show a button as off/not-usable */ +a.button_off:link,a.button_off:visited, a.button_off:active { + color:#C1C1C1; + font-size:12px; + background-color:#ffffff; + text-decoration:none; +} \ No newline at end of file diff --git a/app/webroot/js/deactivateButtons.js b/app/webroot/js/deactivateButtons.js new file mode 100755 index 000000000..8e330f4d7 --- /dev/null +++ b/app/webroot/js/deactivateButtons.js @@ -0,0 +1,101 @@ + \ No newline at end of file From 0eb52796ae094118a8cfe908bafc38605629890c Mon Sep 17 00:00:00 2001 From: noud Date: Fri, 29 Jun 2012 08:56:00 +0200 Subject: [PATCH 2/5] Admin Paginator fix. --- app/Config/routes.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Config/routes.php b/app/Config/routes.php index d3bd24e3f..58e312740 100644 --- a/app/Config/routes.php +++ b/app/Config/routes.php @@ -27,7 +27,11 @@ */ Router::connect('/', array('controller' => 'events', 'action' => 'index')); - + // admin Paginator + Router::connect('/users/admin_index/*', array('controller' => 'users', 'action' => 'index', 'admin' => true)); + Router::connect('/groups/admin_index/*', array('controller' => 'groups', 'action' => 'index', 'admin' => true)); + Router::connect('/logs/admin_index/*', array('controller' => 'logs', 'action' => 'index', 'admin' => true)); + // Activate REST Router::mapResources(array('events')); Router::parseExtensions('xml'); From 9c1c32f959c76e745749bc8abd67f2c0f13bd77f Mon Sep 17 00:00:00 2001 From: noud Date: Fri, 29 Jun 2012 09:36:47 +0200 Subject: [PATCH 3/5] Audit and Access Control granulation in News page. --- app/View/Users/news.ctp | 8 ++++++++ 1 file changed, 8 insertions(+) mode change 100644 => 100755 app/View/Users/news.ctp diff --git a/app/View/Users/news.ctp b/app/View/Users/news.ctp old mode 100644 new mode 100755 index 1a1dc17a8..a1387203e --- a/app/View/Users/news.ctp +++ b/app/View/Users/news.ctp @@ -1,5 +1,13 @@

    News

    +

    June 2012

    +

    Audit
    +There is now log on every login, logout, addition, change and deletion.
    +

    +

    Access Control granulation
    +There is Access Control, an user must be bound to a users-group,
    +so we are able to grant global add, modify and publish rights.
    +

    April 2012

    REST API (output)
    From now on you can use the REST API that uses this XML export. For more information check out the export page.
    From 5bac9ac9284b7e0c15cecc7692c90090d3b37d8a Mon Sep 17 00:00:00 2001 From: noud Date: Mon, 2 Jul 2012 12:52:57 +0200 Subject: [PATCH 4/5] Forgot LogableBehavior in the first commit. --- app/Model/Behavior/LogableBehavior.php | 594 +++++++++++++++++++++++++ 1 file changed, 594 insertions(+) create mode 100755 app/Model/Behavior/LogableBehavior.php diff --git a/app/Model/Behavior/LogableBehavior.php b/app/Model/Behavior/LogableBehavior.php new file mode 100755 index 000000000..747f03530 --- /dev/null +++ b/app/Model/Behavior/LogableBehavior.php @@ -0,0 +1,594 @@ + (Alek), age (28) => (29)] or [name, age] + * + * - "version_id" [int] : cooperates with RevisionBehavior to link the the shadow table (thus linking to old data) + * + * Remember that Logable behavior needs to be added after RevisionBehavior. In fact, just put it last to be safe. + * + * Optionally register what user was responisble for the activity : + * + * - Supply configuration only if defaults are wrong. Example given with defaults : + * + * class Apple extends AppModel { + * var $name = 'Apple'; + * var $actsAs = array('Logable' => array('userModel' => 'User', 'userKey' => 'user_id')); + * [..] + * + * - In AppController (or single controller if only needed once) add these lines to beforeFilter : + * + * if (sizeof($this->uses) && $this->{$this->modelClass}->Behaviors->attached('Logable')) { + * $this->{$this->modelClass}->setUserData($this->activeUser); + * } + * + * This is not used any longer, as AuthComponent collect the user data instead. + * + * Where "$activeUser" should be an array in the standard format for the User model used : + * + * $activeUser = array( $UserModel->alias => array( $UserModel->primaryKey => 123, $UserModel->displayField => 'Alexander')); + * // any other key is just ignored by this behaviour. + * + * @author Alexander Morland (alexander#maritimecolours.no) + * @co-author Eskil Mjelva Saatvedt + * @co-author Ronny Vindenes + * @co-author Carl Erik Fyllingen + * @contributor Miha + * @category Behavior + * @version 2.3 + * @modified 15.november 2011 by Eskil + */ + +class LogableBehavior extends ModelBehavior { + + public $user = NULL; + + public $UserModel = FALSE; + + public $settings = array(); + + public $defaults = array( +'enabled' => true, +'userModel' => 'User', +'userKey' => 'user_id', +'change' => 'list', +'description_ids' => TRUE, +'skip' => array(), +'ignore' => array(), +'classField' => 'model', +'foreignKey' => 'model_id'); + + public $schema = array(); + + /** + * Cake called intializer + * Config options are : + * userModel : 'User'. Class name of the user model you want to use (User by default), if you want to save User in log + * userKey : 'user_id'. The field for saving the user to (user_id by default). + * change : 'list' > [name, age]. Set to 'full' for [name (alek) => (Alek), age (28) => (29)] + * description_ids : TRUE. Set to FALSE to not include model id and user id in the title field + * skip : array(). String array of actions to not log + * + * @param Object $Model + * @param array $config + */ + function setup(&$Model, $config = array()) { + + if (!is_array($config)) { + $config = array(); + } + $this->settings[$Model->alias] = array_merge($this->defaults, $config); + $this->settings[$Model->alias]['ignore'][] = $Model->primaryKey; + + $this->Log = & ClassRegistry::init('Log'); + if ($this->settings[$Model->alias]['userModel'] != $Model->alias) { + $this->UserModel = & ClassRegistry::init($this->settings[$Model->alias]['userModel']); + } else { + $this->UserModel = $Model; + } + $this->schema = $this->Log->schema(); + App::import('Component', 'Auth'); + $this->user[$this->settings[$Model->alias]['userModel']] = AuthComponent::user(); + } + + function settings(&$Model) { + + return $this->settings[$Model->alias]; + } + + function enableLog(&$Model, $enable = null) { + + if ($enable !== null) { + $this->settings[$Model->alias]['enabled'] = $enable; + } + return $this->settings[$Model->alias]['enabled']; + } + + /** + * Useful for getting logs for a model, takes params to narrow find. + * This method can actually also be used to find logs for all models or + * even another model. Using no params will return all activities for + * the models it is called from. + * + * Possible params : + * 'model' : mixed (NULL) String with className, NULL to get current or FALSE to get everything + * 'action' : string (NULL) String with action (add/edit/delete), NULL gets all + * 'order' : string ('created DESC') String with custom order + * 'conditions : array (array()) Add custom conditions + * 'model_id' : int (NULL) Add a int + * + * (remember to use your own user key if you're not using 'user_id') + * 'user_id' : int (NULL) Defaults to all users, supply id if you want for only one User + * + * @param Object $Model + * @param array $params + * @return array + */ + function findLog(&$Model, $params = array()) { + + $defaults = array( + $this->settings[$Model->alias]['classField'] => NULL, +'action' => NULL, +'order' => 'created DESC', + $this->settings[$Model->alias]['userKey'] => NULL, +'conditions' => array(), + $this->settings[$Model->alias]['foreignKey'] => NULL, +'fields' => array(), +'limit' => 50); + $params = array_merge($defaults, $params); + $options = array( +'order' => $params['order'], +'conditions' => $params['conditions'], +'fields' => $params['fields'], +'limit' => $params['limit']); + if ($params[$this->settings[$Model->alias]['classField']] === NULL) { + $params[$this->settings[$Model->alias]['classField']] = $Model->alias; + } + if ($params[$this->settings[$Model->alias]['classField']]) { + if (isset($this->schema[$this->settings[$Model->alias]['classField']])) { + $options['conditions'][$this->settings[$Model->alias]['classField']] = $params[$this->settings[$Model->alias]['classField']]; + } elseif (isset($this->schema['description'])) { + $options['conditions']['description LIKE '] = $params[$this->settings[$Model->alias]['classField']] . '%'; + } else { + return FALSE; + } + } + if ($params['action'] && isset($this->schema['action'])) { + $options['conditions']['action'] = $params['action']; + } + if ($params[$this->settings[$Model->alias]['userKey']] && $this->UserModel && is_numeric($params[$this->settings[$Model->alias]['userKey']])) { + $options['conditions'][$this->settings[$Model->alias]['userKey']] = $params[$this->settings[$Model->alias]['userKey']]; + } + if ($params[$this->settings[$Model->alias]['foreignKey']] && is_numeric($params[$this->settings[$Model->alias]['foreignKey']])) { + $options['conditions'][$this->settings[$Model->alias]['foreignKey']] = $params[$this->settings[$Model->alias]['foreignKey']]; + } + return $this->Log->find('all', $options); + } + + /** + * Get list of actions for one user. + * Params for getting (one line) activity descriptions + * and/or for just one model + * + * @example $this->Model->findUserActions(301,array('model' => 'BookTest')); + * @example $this->Model->findUserActions(301,array('events' => true)); + * @example $this->Model->findUserActions(301,array('fields' => array('id','model'),'model' => 'BookTest'); + * @param Object $Model + * @param int $user_id + * @param array $params + * @return array + */ + function findUserActions(&$Model, $user_id, $params = array()) { + + if (!$this->UserModel) { + return NULL; + } + // if logged in user is asking for her own log, use the data we allready have + if (isset($this->user) && isset($this->user[$this->UserModel->alias][$this->UserModel->primaryKey]) && $user_id == $this->user[$this->UserModel->alias][$this->UserModel->primaryKey] && isset($this->user[$this->UserModel->alias][$this->UserModel->displayField])) { + $username = $this->user[$this->UserModel->alias][$this->UserModel->displayField]; + } else { + $this->UserModel->recursive = -1; + $user = $this->UserModel->find(array( + $this->UserModel->primaryKey => $user_id)); + $username = $user[$this->UserModel->alias][$this->UserModel->displayField]; + } + $fields = array(); + if (isset($params['fields'])) { + if (is_array($params['fields'])) { + $fields = $params['fields']; + } else { + $fields = array( + $params['fields']); + } + } + $conditions = array( + $this->settings[$Model->alias]['userKey'] => $user_id); + if (isset($params[$this->settings[$Model->alias]['classField']])) { + $conditions[$this->settings[$Model->alias]['classField']] = $params[$this->settings[$Model->alias]['classField']]; + } + $data = $this->Log->find('all', array( +'conditions' => $conditions, +'recursive' => -1, +'fields' => $fields)); + if (!isset($params['events']) || ( isset($params['events']) && $params['events'] == false )) { + return $data; + } + $result = array(); + foreach ( $data as $key => $row ) { + $one = $row['Log']; + $result[$key]['Log']['id'] = $one['id']; + $result[$key]['Log']['event'] = $username; + // have all the detail models and change as list : + if (isset($one[$this->settings[$Model->alias]['classField']]) && isset($one['action']) && isset($one['change']) && isset($one[$this->settings[$Model->alias]['foreignKey']])) { + if ($one['action'] == 'edit') { + $result[$key]['Log']['event'] .= ' edited ' . $one['change'] . ' of ' . low($one[$this->settings[$Model->alias]['classField']]) . '(id ' . $one[$this->settings[$Model->alias]['foreignKey']] . ')'; + + // ' at '.$one['created']; + } elseif ($one['action'] == 'add') { + $result[$key]['Log']['event'] .= ' added a ' . low($one[$this->settings[$Model->alias]['classField']]) . '(id ' . $one[$this->settings[$Model->alias]['foreignKey']] . ')'; + } elseif ($one['action'] == 'delete') { + $result[$key]['Log']['event'] .= ' deleted the ' . low($one[$this->settings[$Model->alias]['classField']]) . '(id ' . $one[$this->settings[$Model->alias]['foreignKey']] . ')'; + } + + } elseif (isset($one[$this->settings[$Model->alias]['classField']]) && isset($one['action']) && isset($one[$this->settings[$Model->alias]['foreignKey']])) { // have model,model_id and action + if ($one['action'] == 'edit') { + $result[$key]['Log']['event'] .= ' edited ' . low($one[$this->settings[$Model->alias]['classField']]) . '(id ' . $one[$this->settings[$Model->alias]['foreignKey']] . ')'; + + // ' at '.$one['created']; + } elseif ($one['action'] == 'add') { + $result[$key]['Log']['event'] .= ' added a ' . low($one[$this->settings[$Model->alias]['classField']]) . '(id ' . $one[$this->settings[$Model->alias]['foreignKey']] . ')'; + } elseif ($one['action'] == 'delete') { + $result[$key]['Log']['event'] .= ' deleted the ' . low($one[$this->settings[$Model->alias]['classField']]) . '(id ' . $one[$this->settings[$Model->alias]['foreignKey']] . ')'; + } + } else { // only description field exist + $result[$key]['Log']['event'] = $one['description']; + } + + } + return $result; + } + + /** + * Use this to supply a model with the data of the logged in User. + * Intended to be called in AppController::beforeFilter like this : + * + * if ($this->{$this->modelClass}->Behaviors->attached('Logable')) { + * $this->{$this->modelClass}->setUserData($activeUser);/ + * } + * + * The $userData array is expected to look like the result of a + * User::find(array('id'=>123)); + * + * @param Object $Model + * @param array $userData + */ + function setUserData(&$Model, $userData = null) { + + if ($userData) { + $this->user = $userData; + } + } + + /** + * Used for logging custom actions that arent crud, like login or download. + * + * @example $this->Boat->customLog('ship', 66, array('title' => 'Titanic heads out')); + * @param Object $Model + * @param string $action name of action that is taking place (dont use the crud ones) + * @param int $id id of the logged item (ie model_id in logs table) + * @param array $values optional other values for your logs table + */ + function customLog(&$Model, $action, $id, $values = array()) { + + $logData['Log'] = $values; + /** @todo clean up $logData */ + if (isset($this->schema[$this->settings[$Model->alias]['foreignKey']]) && is_numeric($id)) { + $logData['Log'][$this->settings[$Model->alias]['foreignKey']] = $id; + } + $title = NULL; + if (isset($values['title'])) { + $title = $values['title']; + unset($logData['Log']['title']); + } + $logData['Log']['action'] = $action; + $this->_saveLog($Model, $logData, $title); + } + + function clearUserData(&$Model) { + + $this->user = NULL; + } + + function setUserIp(&$Model, $userIP = null) { + + $this->userIP = $userIP; + } + + function beforeDelete(&$Model) { + + if (!$this->settings[$Model->alias]['enabled']) { + return true; + } + if (isset($this->settings[$Model->alias]['skip']['delete']) && $this->settings[$Model->alias]['skip']['delete']) { + return true; + } + $Model->recursive = -1; + $Model->read(); + return true; + } + + function afterDelete(&$Model) { + + if (!$this->settings[$Model->alias]['enabled']) { + return true; + } + if (isset($this->settings[$Model->alias]['skip']['delete']) && $this->settings[$Model->alias]['skip']['delete']) { + return true; + } + $logData = array(); + if (isset($this->schema['description'])) { + $logData['Log']['description'] = $Model->alias; + if (isset($Model->data[$Model->alias][$Model->displayField]) && $Model->displayField != $Model->primaryKey) { + $logData['Log']['description'] .= ' "' . $Model->data[$Model->alias][$Model->displayField] . '"'; + } + if ($this->settings[$Model->alias]['description_ids']) { + $logData['Log']['description'] .= ' (' . $Model->id . ') '; + } + $logData['Log']['description'] .= __('deleted', TRUE); + } + $logData['Log']['action'] = 'delete'; + $this->_saveLog($Model, $logData); + } + + function beforeSave(&$Model) { + + if (isset($this->schema['change']) && $Model->id) { + $this->old = $Model->find('first', array( +'conditions' => array( + $Model->alias . '.' . $Model->primaryKey => $Model->id), +'recursive' => -1)); + } + return true; + } + + function afterSave(&$Model, $created) { + + if (!$this->settings[$Model->alias]['enabled']) { + return true; + } + if (isset($this->settings[$Model->alias]['skip']['add']) && $this->settings[$Model->alias]['skip']['add'] && $created) { + return true; + } elseif (isset($this->settings[$Model->alias]['skip']['edit']) && $this->settings[$Model->alias]['skip']['edit'] && !$created) { + return true; + } + $keys = array_keys($Model->data[$Model->alias]); + $diff = array_diff($keys, $this->settings[$Model->alias]['ignore']); + if (sizeof($diff) == 0 && empty($Model->logableAction)) { + return false; + } + if ($Model->id) { + $id = $Model->id; + } elseif ($Model->insertId) { + $id = $Model->insertId; + } + if (isset($this->schema[$this->settings[$Model->alias]['foreignKey']])) { + $logData['Log'][$this->settings[$Model->alias]['foreignKey']] = $id; + } + if (isset($this->schema['description'])) { + $logData['Log']['description'] = $Model->alias . ' '; + if (isset($Model->data[$Model->alias][$Model->displayField]) && $Model->displayField != $Model->primaryKey) { + $logData['Log']['description'] .= '"' . $Model->data[$Model->alias][$Model->displayField] . '" '; + } + + if ($this->settings[$Model->alias]['description_ids']) { + $logData['Log']['description'] .= '(' . $id . ') '; + } + + if ($created) { + $logData['Log']['description'] .= __('added', TRUE); + } else { + $logData['Log']['description'] .= __('updated', TRUE); + } + } + if (isset($this->schema['action'])) { + if ($created) { + $logData['Log']['action'] = 'add'; + } else { + $logData['Log']['action'] = 'edit'; + } + + } + if (isset($this->schema['change'])) { + $logData['Log']['change'] = ''; + $db_fields = array_keys($Model->schema()); + $changed_fields = array(); + foreach ( $Model->data[$Model->alias] as $key => $value ) { + if (isset($Model->data[$Model->alias][$Model->primaryKey]) && !empty($this->old) && isset($this->old[$Model->alias][$key])) { + $old = $this->old[$Model->alias][$key]; + } else { + $old = ''; + } + // TODO Audit, removed 'revision' as well + if ($key != 'revision' && $key != 'modified' && !in_array($key, $this->settings[$Model->alias]['ignore']) && $value != $old && in_array($key, $db_fields)) { + if ($this->settings[$Model->alias]['change'] == 'full') { + $changed_fields[] = $key . ' (' . $old . ') => (' . $value . ')'; + } else if ($this->settings[$Model->alias]['change'] == 'serialize') { + $changed_fields[$key] = array( +'old' => $old, +'value' => $value); + } else { + $changed_fields[] = $key; + } + } + } + $changes = sizeof($changed_fields); + if ($changes == 0) { + return true; + } + if ($this->settings[$Model->alias]['change'] == 'serialize') { + $logData['Log']['change'] = serialize($changed_fields); + } else { + $logData['Log']['change'] = implode(', ', $changed_fields); + } + $logData['Log']['changes'] = $changes; + } + $this->_saveLog($Model, $logData); + } + + /** + * Does the actual saving of the Log model. Also adds the special field if possible. + * + * If model field in table, add the Model->alias + * If action field is NOT in table, remove it from dataset + * If the userKey field in table, add it to dataset + * If userData is supplied to model, add it to the title + * + * @param Object $Model + * @param array $logData + */ + function _saveLog(&$Model, $logData, $title = null) { + + if ($title !== NULL) { + $logData['Log']['title'] = $title; + } elseif ($Model->displayField == $Model->primaryKey) { + $logData['Log']['title'] = $Model->alias . ' (' . $Model->id . ')'; + } elseif (isset($Model->data[$Model->alias][$Model->displayField])) { + $logData['Log']['title'] = $Model->data[$Model->alias][$Model->displayField]; + } else { + $logData['Log']['title'] = $Model->field($Model->displayField); + } + + if (isset($this->schema[$this->settings[$Model->alias]['classField']])) { + // by miha nahtigal + $logData['Log'][$this->settings[$Model->alias]['classField']] = $Model->name; + } + + if (isset($this->schema[$this->settings[$Model->alias]['foreignKey']]) && !isset($logData['Log'][$this->settings[$Model->alias]['foreignKey']])) { + if ($Model->id) { + $logData['Log'][$this->settings[$Model->alias]['foreignKey']] = $Model->id; + } elseif ($Model->insertId) { + $logData['Log'][$this->settings[$Model->alias]['foreignKey']] = $Model->insertId; + } + } + + if (!isset($this->schema['action'])) { + unset($logData['Log']['action']); + } elseif (isset($Model->logableAction) && !empty($Model->logableAction)) { + $logData['Log']['action'] = implode(',', $Model->logableAction); // . ' ' . $logData['Log']['action']; + unset($Model->logableAction); + } + + if (isset($this->schema['version_id']) && isset($Model->version_id)) { + $logData['Log']['version_id'] = $Model->version_id; + unset($Model->version_id); + } + + if (isset($this->schema['ip']) && $this->userIP) { + $logData['Log']['ip'] = $this->userIP; + } + + if (isset($this->schema[$this->settings[$Model->alias]['userKey']]) && $this->user) { + $logData['Log'][$this->settings[$Model->alias]['userKey']] = $this->user[$this->UserModel->alias][$this->UserModel->primaryKey]; + } + + if (isset($this->schema['description'])) { + if ($this->user && $this->UserModel) { + $logData['Log']['description'] .= ' by ' . $this->settings[$Model->alias]['userModel'] . ' "' . $this->user[$this->UserModel->alias][$this->UserModel->displayField] . '"'; + if ($this->settings[$Model->alias]['description_ids']) { + $logData['Log']['description'] .= ' (' . $this->user[$this->UserModel->alias][$this->UserModel->primaryKey] . ')'; + } + + } else { + // UserModel is active, but the data hasnt been set. Assume system action. + $logData['Log']['description'] .= ' by System'; + } + $logData['Log']['description'] .= '.'; + } + if (isset($this->schema['email'])) { // TODO Audit, LogableBehevior email + if ($this->user && $this->UserModel) { + $logData['Log']['email'] = $this->user[$this->UserModel->alias][$this->UserModel->displayField]; + } else { + // UserModel is active, but the data hasnt been set. Assume system action. + $logData['Log']['email'] = 'SYS'; + } + } + if (isset($this->schema['org'])) { // TODO Audit, LogableBehevior org CHECK!!! + if ($this->user && $this->UserModel) { + $logData['Log']['org'] = $this->user[$this->UserModel->alias][$this->UserModel->orgField]; + } else { + // UserModel is active, but the data hasnt been set. Assume system action. + $logData['Log']['org'] = 'SYS'; + } + } + if (isset($this->schema['title'])) { // TODO LogableBehevior title + if ($this->user && $this->UserModel) { // $Model->data[$Model->alias][$Model->displayField] + switch ($Model->alias) { + case "User": // TODO Audit, not used here but done in UsersController + $title = 'User ('. $Model->data[$Model->alias]['id'].') '. $Model->data[$Model->alias]['email']; + break; + case "Event": + $title = 'Event ('. $Model->data[$Model->alias]['id'].'): '. $Model->data[$Model->alias]['info']; + $logData['Log']['title'] = $title; + break; + case "Attribute": + if (isset($Model->combinedKeys)) { + if (is_array($Model->combinedKeys)) { + $title = 'Attribute ('. $Model->data[$Model->alias]['id'].') '.'from Event ('. $Model->data[$Model->alias]['event_id'].'): '. $Model->data[$Model->alias][$Model->combinedKeys[1]].'/'. $Model->data[$Model->alias][$Model->combinedKeys[2]].' '. $Model->data[$Model->alias]['value1']; + $logData['Log']['title'] = $title; + } + } + break; + case "Server": + $title = 'Server ('. $Model->data[$Model->alias]['id'].'): '. $Model->data[$Model->alias]['url']; + $logData['Log']['title'] = $title; + break; + default: + if (isset($Model->combinedKeys)) { + if (is_array($Model->combinedKeys)) { + $title = ''; + foreach ($Model->combinedKeys as $combinedKey) { + $title .= '/'. $Model->data[$Model->alias][$combinedKey]; + } + $title = substr($title ,1); + $logData['Log']['title'] = $title; + } + } + } + } + } + $this->Log->create($logData); + $this->Log->save(null, array( +'validate' => false, +'callbacks' => false)); + + // write to syslogd as well + $syslog = new SysLog(); + $syslog->write('notice', $logData['Log']['description'].' -- '.$logData['Log']['change']); + } +} \ No newline at end of file From e2df7442a4e41266fa236b925e08cfda255a644b Mon Sep 17 00:00:00 2001 From: Andrzej Dereszowski Date: Mon, 2 Jul 2012 16:26:50 +0200 Subject: [PATCH 5/5] shit --- .gitignore | 2 ++ app/Config/bootstrap.php | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c65c367a2..8130153d1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ /app/tmp/logs/*.log /app/files/* /app/webroot/img/logo.png +/app/Config/bootstrap.php +/app/Config/database.php diff --git a/app/Config/bootstrap.php b/app/Config/bootstrap.php index 9a30d725c..f9dc2d99e 100755 --- a/app/Config/bootstrap.php +++ b/app/Config/bootstrap.php @@ -142,5 +142,6 @@ Configure::write('SecureAuth.expire', 300); // the time-window for th * CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit * */ + CakePlugin::load('SysLog'); -CakePlugin::load('AclExtras'); \ No newline at end of file +CakePlugin::load('AclExtras');

    Paginator->sort('id');?> Paginator->sort('org');?>Paginator->sort('group_id');?> Paginator->sort('email');?> Paginator->sort('autoalert');?> Paginator->sort('gpgkey');?>   + Html->link($user['Group']['name'], array('controller' => 'groups', 'action' => 'view', $user['Group']['id'])); ?>     Html->link(__('View'), array('admin' => true, 'action' => 'view', $user['User']['id'])); ?> - Html->link(__('Edit'), array('admin' => true, 'action' => 'edit', $user['User']['id'])); ?> - Form->postLink(__('Delete'), array('admin' => true, 'action' => 'delete', $user['User']['id']), null, __('Are you sure you want to delete # %s?', $user['User']['id'])); ?> + Html->link(__('Edit'), array('admin' => true, 'action' => 'edit', $user['User']['id']), $isAclModify||($user['User']['org'] == $me['org']) ? null:array('id' => $button_modify_status.$buttonCounter++, 'class' => $button_modify_status)); ?> + Form->postLink(__('Delete'), array('admin' => true, 'action' => 'delete', $user['User']['id']), null, __('Are you sure you want to delete # %s?', $user['User']['id'])); + else echo $this->Html->link(__('Delete'), array('admin' => true, 'action' => 'delete', $user['User']['id']), array('id' => $button_modify_status.$buttonCounter++,'class' => $button_modify_status)); + ?>
    Html->link(__('View'), array('controller' => 'events', 'action' => 'view', $event['id'])); ?> - Html->link(__('Edit'), array('controller' => 'events', 'action' => 'edit', $event['id'])); ?> - Form->postLink(__('Delete'), array('controller' => 'events', 'action' => 'delete', $event['id']), null, __('Are you sure you want to delete # %s?', $event['id'])); ?> + Html->link(__('Edit'), array('controller' => 'events', 'action' => 'edit', $event['id']), array('id' => $button_modify_status.$buttonCounter++,'class' => $button_modify_status)); ?> + Form->postLink(__('Delete'), array('controller' => 'events', 'action' => 'delete', $event['id']), null, __('Are you sure you want to delete # %s?', $event['id'])); + else echo $this->Html->link(__('Delete'), array('controller' => 'events', 'action' => 'delete', $event['id']), array('id' => $button_modify_status.$buttonCounter++,'class' => $button_modify_status)); + ?>