diff --git a/.gitignore b/.gitignore index d6ce1a071..10d43b40d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ /app/tmp/cache/views/myapp* /app/files/* /app/webroot/img/logo.png +/app/Config/bootstrap.php +/app/Config/database.php 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 index f08502ab9..0674df73e 100644 --- a/app/Config/Schema/schema_0.2.2.php +++ b/app/Config/Schema/schema_0.2.2.php @@ -1,6 +1,8 @@ - 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.default.php b/app/Config/bootstrap.default.php index 525dd8e86..e31a08d49 100644 --- a/app/Config/bootstrap.default.php +++ b/app/Config/bootstrap.default.php @@ -111,6 +111,10 @@ Configure::write('CyDefSIG.logo', 'orgs/MIL.be.png'); // used in Events::ind Configure::write('CyDefSIG.showorg', 'true'); // show the name/flag of the organisation that uploaded the data Configure::write('CyDefSIG.showowner', 'false'); // show the email of the owner that uploaded the data Configure::write('CyDefSIG.sync', 'false'); // enable features related to syncing with other CyDefSIG instances +Configure::write('CyDefSIG.private', 'true'); // respect private to org or server. +if ('true' == Configure::read('CyDefSIG.private')) { + Configure::write('CyDefSIG.sync', 'true'); +} Configure::write('CyDefSIG.email', 'no-reply@sig.mil.be'); // email from for all the mails Configure::write('GnuPG.onlyencrypted', 'true'); // only allow encrypted email, do not allow plaintext mails @@ -183,6 +187,12 @@ Configure::write('CyDefSIG.correlation', 'sql'); // correlation between a * */ +CakePlugin::load('AclExtras'); + +CakePlugin::load('SysLog'); +CakePlugin::load('Assets'); // having Logable +CakePlugin::load('SysLogLogable'); +CakePlugin::load('MagicTools'); // having OrphansProtectable /** * You can attach event listeners to the request lifecyle as Dispatcher Filter . By Default CakePHP bundles two filters: diff --git a/app/Config/routes.php b/app/Config/routes.php old mode 100644 new mode 100755 index 82aa26118..42d48c587 --- a/app/Config/routes.php +++ b/app/Config/routes.php @@ -27,8 +27,10 @@ */ Router::connect('/', array('controller' => 'events', 'action' => 'index')); - // admin pagination + // 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')); 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/cake b/app/Console/cake old mode 100644 new mode 100755 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 index ad9f250d6..7f6aa45c7 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -24,7 +24,6 @@ App::uses('Controller', 'Controller'); App::uses('Sanitize', 'Utility'); - /** * Application Controller * @@ -37,6 +36,7 @@ App::uses('Sanitize', 'Utility'); class AppController extends Controller { public $components = array( + 'Acl', // TODO XXX remove 'Session', 'Auth' => array( 'className' => 'SecureAuth', @@ -48,8 +48,9 @@ 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 + ) ); public function isAuthorized($user) { @@ -94,6 +95,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')); } public function blackhole($type) { @@ -316,7 +322,7 @@ class AppController extends Controller { $this->loadModel('Correlation'); $this->loadModel('Attribute'); - $fields = array('Attribute.id', 'Attribute.event_id', 'Event.date'); + $fields = array('Attribute.id', 'Attribute.event_id', 'Attribute.private', 'Event.date', 'Event.org'); // get all attributes.. $attributes = $this->Attribute->find('all',array('recursive' => 0)); // for all attributes.. @@ -337,4 +343,52 @@ class AppController extends Controller { //} } } + +/** + * TODO ACL, 6b: check on Group and per Model (not used) + */ + public 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 + */ + public 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; + } + } + + public function generatePrivate() { + if (!self::_isAdmin()) throw new NotFoundException(); + + $this->loadModel('Correlation'); + $this->loadModel('Attribute'); + $attributes = $this->Attribute->find('all',array('recursive' => 0)); + foreach ($attributes as $attribute) { + if ($attribute['Attribute']['private']) { + $attribute['Attribute']['private'] = false; + $attribute['Attribute']['pull'] = true; + } + $this->Attribute->save($attribute); + } + + $this->loadModel('Event'); + $events = $this->Event->find('all',array('recursive' => 0)); + foreach ($events as $event) { + if ($event['Event']['private']) { + $event['Event']['private'] = false; + $event['Event']['pull'] = true; + } + $this->Event->save($event); + } + } } diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php old mode 100644 new mode 100755 index 06887015b..36200cc2d --- 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, @@ -41,6 +41,31 @@ class AttributesController extends AppController { $this->params->addParams(array('pass' => array($id))); // FIXME find better way to change id variable if uuid is found. params->url and params->here is not modified accordingly now } } + + // do not show private to other groups + if ('true' == Configure::read('CyDefSIG.private')) { + // if not admin or own org, check private as well.. + if (!$this->_IsAdmin()) { + $this->paginate = Set::merge($this->paginate,array( + 'conditions' => + array("OR" => array( + array('Event.org =' => $this->Auth->user('org')), + array("AND" => array('Event.org !=' => $this->Auth->user('org')), array('Event.private !=' => 1), array('Attribute.private !=' => 1)))), + ) + ); + } + } + + // do not show cluster outside server + if ('true' == Configure::read('CyDefSIG.private')) { + if ($this->_isRest()) { + $this->paginate = Set::merge($this->paginate,array( + 'conditions' => + array("AND" => array('Event.cluster !=' => true),array('Attribute.cluster !=' => true)), + //array("AND" => array(array('Event.private !=' => 2))), + )); + } + } } public function isAuthorized($user) { @@ -49,7 +74,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')); } @@ -114,6 +139,9 @@ class AttributesController extends AppController { $this->Attribute->create(); $this->request->data['Attribute']['value'] = $attribute; // set the value as the content of the single line + if ('true' == Configure::read('CyDefSIG.private')) { + $this->request->data = $this->Attribute->massageData(&$this->request->data); + } if ($this->Attribute->save($this->request->data)) { $successes .= " " . ($key + 1); } else { @@ -140,6 +168,10 @@ class AttributesController extends AppController { // create the attribute $this->Attribute->create(); + if ('true' == Configure::read('CyDefSIG.private')) { + $this->request->data = $this->Attribute->massageData(&$this->request->data); + } + if ($this->Attribute->save($this->request->data)) { // inform the user and redirect $this->Session->setFlash(__('The attribute has been saved')); @@ -162,6 +194,12 @@ class AttributesController extends AppController { $categories = $this->_arrayToValuesIndexArray($categories); $this->set('categories',compact('categories')); + if ('true' == Configure::read('CyDefSIG.private')) { + $sharings = array('Org', 'Server', 'Pull only', 'All'); + $sharings = $this->_arrayToValuesIndexArray($sharings); + $this->set('sharings',compact('sharings')); + } + $this->set('attrDescriptions', $this->Attribute->fieldDescriptions); $this->set('typeDefinitions', $this->Attribute->typeDefinitions); $this->set('categoryDefinitions', $this->Attribute->categoryDefinitions); @@ -183,6 +221,7 @@ class AttributesController extends AppController { } elseif ('malware-sample' == $this->Attribute->data['Attribute']['type']) { $filenameHash = explode('|', $this->Attribute->data['Attribute']['value']); $filename = $filenameHash[0]; + $filename = substr($filenameHash[0], strrpos($filenameHash[0], '\\')); $fileExt = "zip"; } else { throw new NotFoundException(__('Attribute not an attachment or malware-sample')); @@ -191,10 +230,10 @@ class AttributesController extends AppController { $this->viewClass = 'Media'; $params = array( 'id' => $file->path, - 'name' => $filename, + 'name' => $filename, 'extension' => $fileExt, 'download' => true, - 'path' => DS + 'path' => DS ); $this->set($params); } @@ -241,6 +280,9 @@ class AttributesController extends AppController { } $this->request->data['Attribute']['uuid'] = String::uuid(); $this->request->data['Attribute']['batch_import'] = 0; + if ('true' == Configure::read('CyDefSIG.private')) { + $this->request->data = $this->Attribute->massageData(&$this->request->data); + } if ($this->Attribute->save($this->request->data)) { // attribute saved correctly in the db @@ -329,6 +371,12 @@ class AttributesController extends AppController { $this->set('zippedDefinitions', $this->Attribute->zippedDefinitions); $this->set('uploadDefinitions', $this->Attribute->uploadDefinitions); + + if ('true' == Configure::read('CyDefSIG.private')) { + $sharings = array('Org', 'Server', 'Pull only', 'All'); + $sharings = $this->_arrayToValuesIndexArray($sharings); + $this->set('sharings',compact('sharings')); + } } /** @@ -358,8 +406,12 @@ class AttributesController extends AppController { } if ($this->request->is('post') || $this->request->is('put')) { + if ('true' == Configure::read('CyDefSIG.private')) { + $this->request->data = $this->Attribute->massageData(&$this->request->data); + } + // say what fields are to be updated - $fieldList = array('category', 'type', 'value1', 'value2', 'to_ids', 'private'); + $fieldList = array('category', 'type', 'value1', 'value2', 'to_ids', 'private', 'cluster', 'pull'); if ($this->Attribute->save($this->request->data)) { $this->Session->setFlash(__('The attribute has been saved')); @@ -385,6 +437,12 @@ class AttributesController extends AppController { $categories = $this->_arrayToValuesIndexArray($categories); $this->set('categories',compact('categories')); + if ('true' == Configure::read('CyDefSIG.private')) { + $sharings = array('Org', 'Server', 'Pull only', 'All'); + $sharings = $this->_arrayToValuesIndexArray($sharings); + $this->set('sharings',compact('sharings')); + } + $this->set('attrDescriptions', $this->Attribute->fieldDescriptions); $this->set('typeDefinitions', $this->Attribute->typeDefinitions); $this->set('categoryDefinitions', $this->Attribute->categoryDefinitions); @@ -492,6 +550,20 @@ class AttributesController extends AppController { $this->paginate = array( 'conditions' => $conditions ); + + if ('true' == Configure::read('CyDefSIG.private')) { + if (!$this->_IsAdmin()) { + // merge in private conditions + $this->paginate = Set::merge($this->paginate,array( + 'conditions' => + array("OR" => array( + array('Event.org =' => $this->Auth->user('org')), + array("AND" => array('Event.org !=' => $this->Auth->user('org')), array('Event.private !=' => 1), array('Attribute.private !=' => 1)))), + ) + ); + } + } + $this->set('attributes', $this->paginate()); // and store into session diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php old mode 100644 new mode 100755 index 078092eda..f0a9d8412 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -15,6 +15,7 @@ class EventsController extends AppController { * @var array */ public $components = array( + 'Acl', // XXX ACL component 'Security', 'Email', 'RequestHandler', @@ -43,6 +44,13 @@ class EventsController extends AppController { $this->Auth->allow('dot'); + // TODO Audit, activate logable in a Controller + if (count($this->uses) && $this->{$this->modelClass}->Behaviors->attached('SysLogLogable')) { + $this->{$this->modelClass}->setUserData($this->activeUser); + } + + // TODO ACL, if on ent/attr level, $this->set('isAcl', $this->checkAccess()); + // convert uuid to id if present in the url, and overwrite id field if (isset($this->params->query['uuid'])) { $params = array( @@ -56,6 +64,30 @@ class EventsController extends AppController { $this->params->addParams(array('pass' => array($id))); // FIXME find better way to change id variable if uuid is found. params->url and params->here is not modified accordingly now } } + + // do not show private to other groups + if ('true' == Configure::read('CyDefSIG.private')) { + // if not admin or own org, check private as well.. + if (!$this->_IsAdmin()) { + $this->paginate = Set::merge($this->paginate,array( + 'conditions' => + array("OR" => array( + array('Event.org =' => $this->Auth->user('org')), + array("AND" => array('Event.org !=' => $this->Auth->user('org')), array('Event.private !=' => 1)))), + )); + } + } + + // do not show cluster outside server + if ('true' == Configure::read('CyDefSIG.private')) { + if ($this->_isRest()) { + $this->paginate = Set::merge($this->paginate,array( + 'conditions' => + array(array('Event.cluster !=' => true)), + //array("AND" => array(array('Event.private !=' => 2))), + )); + } + } } public function isAuthorized($user) { @@ -64,7 +96,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')); } @@ -102,21 +134,44 @@ class EventsController extends AppController { } $this->Event->read(null, $id); + if ('true' == Configure::read('CyDefSIG.private')) { + if (!$this->_IsAdmin()) { + // check for non-private and re-read + if ($this->Event->data['Event']['org'] != $this->Auth->user('org')) { + $this->Event->hasMany['Attribute']['conditions'] = array('Attribute.private !=' => 1); + $this->Event->read(null, $id); + } + + // check private + if (($this->Event->data['Event']['private']) && ($this->Event->data['Event']['org'] != $this->Auth->user('org'))) { + $this->Session->setFlash('Invalid event.'); + $this->redirect(array('controller' => 'users', 'action' => 'terms')); + } + } + } + $relatedAttributes = array(); $this->loadModel('Attribute'); if ('db' == Configure::read('CyDefSIG.correlation')) { $this->loadModel('Correlation'); $fields = array('Correlation.event_id', 'Correlation.attribute_id', 'Correlation.date'); - $fields2 = array('Correlation.1_attribute_id','Correlation.event_id', 'Correlation.attribute_id', 'Correlation.date'); + $fields2 = array('Correlation.1_attribute_id','Correlation.event_id', 'Correlation.attribute_id', 'Correlation.date', 'Correlation.private', 'Correlation.org'); $relatedAttributes2 = array(); - $relatedAttributes2 = $this->Correlation->find('all',array( - 'fields' => $fields2, - 'conditions' => array( - 'OR' => array( - 'Correlation.1_event_id' => $id - ) - ), - 'recursive' => 0)); + if ('true' == Configure::read('CyDefSIG.private')) { + $conditionsCorrelation = + array('AND' => array('Correlation.1_event_id' => $id,), + array("OR" => array( + array('Correlation.org =' => $this->Event->data['Event']['org']), + array("AND" => array('Correlation.org !=' => $this->Event->data['Event']['org']), array('Correlation.private !=' => 1))))); + } else { + $conditionsCorrelation = + array('AND' => array('Correlation.1_event_id' => $id,)); + } + $relatedAttributes2 = $this->Correlation->find('all',array( + 'fields' => $fields2, + 'conditions' => $conditionsCorrelation, + 'recursive' => 0)); + if (empty($relatedAttributes2)) { $relatedEvents = null; } else { @@ -223,19 +278,44 @@ class EventsController extends AppController { */ public function add() { if ($this->request->is('post')) { - if ($this->_add($this->request->data, $this->Auth, $this->_isRest(),'')) { - if ($this->_isRest()) { - // REST users want to see the newly created event - $this->view($this->Event->getId()); - $this->render('view'); + + // TODO or massageData here + if ('true' == Configure::read('CyDefSIG.private')) { + $this->request->data = $this->Event->massageData(&$this->request->data); + } + + if (!empty($this->data)) { + if (isset($this->data['Event']['submittedfile'])) { + App::uses('File', 'Utility'); + $file = new File($this->data['Event']['submittedfile']['name']); + $ext = $file->ext(); } else { - // redirect to the view of the newly created event - $this->Session->setFlash(__('The event has been saved')); - $this->redirect(array('action' => 'view', $this->Event->getId())); + $ext = ''; + } + if (isset($this->data['Event']['submittedfile']) && $ext != 'zip' && $this->data['Event']['submittedfile']['size'] > 0 && + is_uploaded_file($this->data['Event']['submittedfile']['tmp_name'])) { + //return false; + $this->Session->setFlash('You may only upload GFI Sandbox zip files.'); + } else { + // TODO or massageData here + if ($this->_add($this->request->data, $this->Auth, $this->_isRest(),'')) { + if ($this->_isRest()) { + // REST users want to see the newly created event + $this->view($this->Event->getId()); + $this->render('view'); + } else { + // TODO now save uploaded attributes using $this->Event->getId() .. + $this->addGfiZip($this->Event->getId()); + + // redirect to the view of the newly created event + $this->Session->setFlash(__('The event has been saved')); + $this->redirect(array('action' => 'view', $this->Event->getId())); + } + } else { + $this->Session->setFlash(__('The event could not be saved. Please, try again.'), 'default', array(), 'error'); + // TODO return error if REST + } } - } else { - $this->Session->setFlash(__('The event could not be saved. Please, try again.'), 'default', array(), 'error'); - // TODO return error if REST } } // combobox for risks @@ -243,6 +323,12 @@ class EventsController extends AppController { $risks = $this->_arrayToValuesIndexArray($risks); $this->set('risks',compact('risks')); + if ('true' == Configure::read('CyDefSIG.private')) { + $sharings = array('Org', 'Server', 'Pull only', 'All'); + $sharings = $this->_arrayToValuesIndexArray($sharings); + $this->set('sharings',compact('sharings')); + } + $this->set('eventDescriptions', $this->Event->fieldDescriptions); } @@ -296,9 +382,14 @@ class EventsController extends AppController { } $fieldList = array( - 'Event' => array('org', 'date', 'risk', 'info', 'user_id', 'published', 'uuid', 'private'), - 'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'private') + 'Event' => array('org', 'date', 'risk', 'info', 'user_id', 'published', 'uuid', 'private', 'cluster', 'pull'), + 'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'private', 'cluster', 'pull') ); + + if ('true' == Configure::read('CyDefSIG.private')) { + $data = $this->Event->massageData(&$data); + } + // this saveAssociated() function will save not only the event, but also the attributes // from the attributes attachments are also saved to the disk thanks to the afterSave() fonction of Attribute if ($this->Event->saveAssociated($data, array('validate' => true, 'fieldList' => $fieldList))) { @@ -364,7 +455,7 @@ class EventsController extends AppController { } // say what fields are to be updated - $fieldList = array('date', 'risk', 'info', 'published', 'private'); + $fieldList = array('date', 'risk', 'info', 'published', 'private', 'cluster', 'pull'); // always force the org, but do not force it for admins if ($this->_isAdmin()) { // set the same org as existed before @@ -374,6 +465,10 @@ class EventsController extends AppController { // we probably also want to remove the published flag $this->request->data['Event']['published'] = 0; + if ('true' == Configure::read('CyDefSIG.private')) { + $this->request->data = $this->Event->massageData(&$this->request->data); + } + if ($this->Event->save($this->request->data, true, $fieldList)) { $this->Session->setFlash(__('The event has been saved')); $this->redirect(array('action' => 'view', $id)); @@ -389,7 +484,14 @@ class EventsController extends AppController { $risks = $this->_arrayToValuesIndexArray($risks); $this->set('risks',compact('risks')); + if ('true' == Configure::read('CyDefSIG.private')) { + $sharings = array('Org', 'Server', 'Pull only', 'All'); + $sharings = $this->_arrayToValuesIndexArray($sharings); + $this->set('sharings', compact('sharings')); + } + $this->set('eventDescriptions', $this->Event->fieldDescriptions); + $this->set('privateDefinitions', $this->Event->privateDefinitions); } /** @@ -564,13 +666,13 @@ class EventsController extends AppController { // The mail body, h() is NOT needed as we are sending plain-text mails. $body = ""; $appendlen = 20; - $body .= 'URL : ' . Configure::read('CyDefSIG.baseurl') . '/events/view/' . $event['Event']['id'] . "\n"; - $body .= 'Event : ' . $event['Event']['id'] . "\n"; - $body .= 'Date : ' . $event['Event']['date'] . "\n"; + $body .= 'URL : ' . Configure::read('CyDefSIG.baseurl') . '/events/view/' . $event['Event']['id'] . "\n"; + $body .= 'Event : ' . $event['Event']['id'] . "\n"; + $body .= 'Date : ' . $event['Event']['date'] . "\n"; if ('true' == Configure::read('CyDefSIG.showorg')) { $body .= 'Reported by : ' . $event['Event']['org'] . "\n"; } - $body .= 'Risk : ' . $event['Event']['risk'] . "\n"; + $body .= 'Risk : ' . $event['Event']['risk'] . "\n"; $relatedEvents = $this->Event->getRelatedEvents($id); if (!empty($relatedEvents)) { foreach ($relatedEvents as &$relatedEvent) { @@ -586,7 +688,7 @@ class EventsController extends AppController { if (isset($event['Attribute'])) { foreach ($event['Attribute'] as &$attribute) { - $line = '- ' . $attribute['type'] . str_repeat(' ', $appendlen - 2 - strlen( $attribute['type'])) . ': ' . $attribute['value'] . "\n"; + $line = '- ' . $attribute['type'] . str_repeat(' ', $appendlen - 2 - strlen($attribute['type'])) . ': ' . $attribute['value'] . "\n"; if ('other' == $attribute['type']) // append the 'other' attribute types to the bottom. $bodyTempOther .= $line; else $body .= $line; @@ -613,7 +715,7 @@ class EventsController extends AppController { 'conditions' => array('User.autoalert' => 1, 'User.gpgkey =' => ""), 'recursive' => 0, - ) ); + )); $alertEmails = Array(); foreach ($alertUsers as &$user) { $alertEmails[] = $user['User']['email']; @@ -624,7 +726,7 @@ class EventsController extends AppController { $this->Email->bcc = $alertEmails; $this->Email->subject = "[" . Configure::read('CyDefSIG.name') . "] Event " . $id . " - " . $event['Event']['risk'] . " - TLP Amber"; $this->Email->template = 'body'; - $this->Email->sendAs = 'text'; // both text or html + $this->Email->sendAs = 'text'; // both text or html $this->set('body', $bodySigned); // send it $this->Email->send(); @@ -649,7 +751,7 @@ class EventsController extends AppController { $this->Email->to = $user['User']['email']; $this->Email->subject = "[" . Configure::read('CyDefSIG.name') . "] Event " . $id . " - " . $event['Event']['risk'] . " - TLP Amber"; $this->Email->template = 'body'; - $this->Email->sendAs = 'text'; // both text or html + $this->Email->sendAs = 'text'; // both text or html // import the key of the user into the keyring // this is not really necessary, but it enables us to find @@ -1101,4 +1203,205 @@ class EventsController extends AppController { // debug($gv); // $gv->image(); //} + + public function getName($id = null) { + $events = $this->Event->find('first', array( + 'conditions' => array('Event.id' => $id) + )); + $name = $events['Event']['info']; + return $name; + } + + public function addGfiZip($id) { + if (!empty($this->data) && $this->data['Event']['submittedfile']['size'] > 0 && + is_uploaded_file($this->data['Event']['submittedfile']['tmp_name'])) { + $zipData = fread(fopen($this->data['Event']['submittedfile']['tmp_name'], "r"), + $this->data['Event']['submittedfile']['size']); + + // write + $rootDir = APP . "files" . DS . $id . DS; + App::uses('Folder', 'Utility'); + $dir = new Folder($rootDir, true); + $destpath = $rootDir; + $file = new File ($destpath); + $zipfile = new File ($destpath . DS . $this->data['Event']['submittedfile']['name']); + $result = $zipfile->write($zipData); + if (!$result) $this->Session->setFlash(__('Problem with writing the zip file. Please report to administrator.')); + + // extract zip.. + $execRetval = ''; + exec("unzip " . $zipfile->path . ' -d "' . addslashes($rootDir) . '"', $execOutput, $execRetval); + $execOutput = array(); + if ($execRetval != 0) { // not EXIT_SUCCESS + // do some? + } + + // now open the xml.. + $xml = $rootDir . DS . 'Analysis' . DS . 'analysis.xml'; + $fileData = fread(fopen($xml, "r"), $this->data['Event']['submittedfile']['size']); + + // read XML + $this->readGfiXML($fileData, $id); + } + } + + public function readGfiXML($data, $id) { + $this->loadModel('Attribute'); + + // import XML class + App::uses('Xml', 'Utility'); + // now parse it + $parsedXml =& Xml::build($data, array('return' => 'simplexml')); + + // xpath.. + + //Payload delivery -- malware-sample + $results = $parsedXml->xpath('/analysis'); + foreach ($results as $result) { + foreach ($result[0]->attributes() as $key => $val) { + if ((string)$key == 'filename') $realFileName = (string)$val; + } + } + $realMalware = $realFileName; + $rootDir = APP . "files" . DS . $id . DS; + $malware = $rootDir . DS . 'sample'; + $this->Event->Attribute->uploadAttachment($malware, $realFileName, true, $id); + + //Network activity -- .pcap + $realFileName = 'analysis.pcap'; + $rootDir = APP . "files" . DS . $id . DS; + $malware = $rootDir . DS . 'Analysis' . DS . 'analysis.pcap'; + $this->Event->Attribute->uploadAttachment($malware, $realFileName, false, $id, 'Network activity'); + + //Artifacts dropped -- filename|md5 + $files = array(); + // TODO what about stored_modified_file ?? + $results = $parsedXml->xpath('/analysis/processes/process/stored_files/stored_created_file'); + foreach ($results as $result) { + $arrayItemKey = ''; + $arrayItemValue = ''; + foreach ($result[0]->attributes() as $key => $val) { + if ($key == 'filename') $arrayItemKey = (string)$val; + if ($key == 'md5') $arrayItemValue = (string)$val; + } + + $files[$arrayItemKey] = $arrayItemValue; + } + //$files = array_unique($files); + + // write content.. + foreach ($files as $key => $val) { + $keyName = $key; + // replace Windows Environment Variables + $keyName = str_replace('C:\Users\John', '%UserProfile%', $keyName); + $keyName = str_replace('C:\Documents and Settings\James Cocks', '%UserProfile%', $keyName); + $keyName = str_replace('C:\DOCUME~1\JAMESC~1', '%UserProfile%', $keyName); + $keyName = str_replace('C:\Documents and Settings\All Users', '%AllUsersProfile%', $keyName); + + if (!strpos($key, $realMalware)) { + $itsType = 'malware-sample'; + } else { + $itsType = 'filename|md5'; + } + + // the actual files.. + // seek $val in dirs and add.. + $ext = substr($key, strrpos($key, '.')); + $actualFileName = $val . $ext; + $actualFileNameBase = str_replace('\\', '/', $key); + $actualFileNameArray[] = basename($actualFileNameBase); + $realFileName = end(explode('\\', $key)); + // have the filename, now look at parents parent for the process number + $express = "/analysis/processes/process/stored_files/stored_created_file[@md5='" . $val . "']/../.."; + $results = $parsedXml->xpath($express); + foreach ($results as $result) { + foreach ($result[0]->attributes() as $key => $val) { + if ((string)$key == 'index') $index = (string)$val; + } + } + $actualFile = $rootDir . DS . 'Analysis' . DS . 'proc_' . $index . DS . 'modified_files' . DS . $actualFileName; + $extraPath = 'Analysis' . DS . 'proc_' . $index . DS . 'modified_files' . DS; + $file = new File($actualFile); + if ($file->exists()) { // TODO put in array for test later + $this->Event->Attribute->uploadAttachment($actualFile, $realFileName, true, $id, null, $extraPath, $keyName); // TODO was false + } + } + + //Network activity -- ip-dst + $ips = array(); + $results = $parsedXml->xpath('/analysis/processes/process/networkpacket_section/connect_to_computer'); + foreach ($results as $result) { + foreach ($result[0]->attributes() as $key => $val) { + if ($key == 'remote_ip') $ips[] = (string)$val; + } + } + // write content.. + foreach ($ips as $ip) { + // add attribute.. + $this->Attribute->read(null, 1); + $this->Attribute->save(array( + 'event_id' => $id, + 'category' => 'Network activity', + 'type' => 'ip-dst', + 'value' => $ip, + 'to_ids' => false)); + } + + // Persistence mechanism -- regkey|value + $regs = array(); + $results = $parsedXml->xpath('/analysis/processes/process/registry_section/set_value'); + foreach ($results as $result) { + $arrayItemKey = ''; + $arrayItemValue = ''; + foreach ($result[0]->attributes() as $key => $val) { + if ($key == 'key_name') $arrayItemKey = (string)$val; + if ($key == 'data') $arrayItemValue = (string)$val; + } + $arrayItemKey = preg_replace('@\\\REGISTRY\\\USER\\\S(-[0-9]{1}){2}-[0-9]{2}(-[0-9]{10}){2}-[0-9]{9}-[0-9]{4}@','HKEY_CURRENT_USER',$arrayItemKey); + $regs[$arrayItemKey] = str_replace('(UNICODE_0x00000000)', '', $arrayItemValue); + } + //$regs = array_unique($regs); + + // write content.. + foreach ($regs as $key => $val) { + // add attribute.. + $this->Attribute->read(null, 1); + if ($val == '[binary_data]') { + $itsCategory = 'Persistence mechanism'; + $itsType = 'regkey'; + $itsValue = $key; + } else { + if ($this->strposarray($val,$actualFileNameArray)) { + $itsCategory = 'Persistence mechanism'; + $itsType = 'regkey|value'; + $itsValue = $key . '|' . $val; + } else { + // replace Windows Environment Variables + $val = str_replace('C:\Users\John', '%UserProfile%', $val); + $val = str_replace('C:\Documents and Settings\James Cocks', '%UserProfile%', $val); + $val = str_replace('C:\DOCUME~1\JAMESC~1', '%UserProfile%', $val); + $val = str_replace('C:\Documents and Settings\All Users', '%AllUsersProfile%', $val); + + $itsCategory = 'Artifacts dropped'; // Persistence mechanism + $itsType = 'regkey|value'; + $itsValue = $key . '|' . $val; + } + } + $this->Attribute->save(array( + 'event_id' => $id, + 'category' => $itsCategory, // 'Persistence mechanism' + 'type' => $itsType, + 'value' => $itsValue, + 'to_ids' => false)); + } + } + public function strposarray($string, $array) { + $toReturn = false; + foreach ($array as $item) { + if (strpos($string,$item)) { + $toReturn = true; + } + } + return $toReturn; + } } diff --git a/app/Controller/GroupsController.php b/app/Controller/GroupsController.php new file mode 100755 index 000000000..d6ef4ba34 --- /dev/null +++ b/app/Controller/GroupsController.php @@ -0,0 +1,178 @@ + 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->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->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(null, false)) { + $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..aab967044 --- /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('actionDefinitions', $this->Log->actionDefinitions); + + 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 100755 new mode 100644 index 46fcf3f88..78b557f0a --- 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, @@ -283,9 +283,15 @@ class ServersController extends AppController { // increment lastid based on the highest ID seen $this->Server->saveField('lastpushedid', $lastpushedid); } - $this->set('successes', $successes); $this->set('fails', $fails); } + public function getName($id = null) { + $servers = $this->Server->find('first', array( + 'conditions' => array('Server.id' => $id) + )); + $name = $servers['Server']['url']; + return $name; + } } diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php old mode 100644 new mode 100755 index 4c82ce691..1ba51153e --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -9,7 +9,7 @@ class UsersController extends AppController { public $newkey; - public $components = array('Security'); + public $components = array('Acl','Security'); // TODO ACL, components public $paginate = array( 'limit' => 60, @@ -94,6 +94,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')); } /** @@ -171,6 +174,9 @@ class UsersController extends AppController { $this->newkey = $this->User->generateAuthKey(); $this->set('authkey', $this->newkey); } + // XXX ACL groups + $groups = $this->User->Group->find('list'); + $this->set(compact('groups')); } /** @@ -190,9 +196,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 + $fieldsOldValues = array(); + foreach ($fields as $field) { + if($field != 'confirm_password') array_push($fieldsOldValues, $this->User->field($field)); + else array_push($fieldsOldValues, $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 + $fieldsNewValues = array(); + foreach ($fields as $field) { + if ($field != 'confirm_password') { + $newValue = $this->data['User'][$field]; + if (gettype($newValue) == 'array') { + $newValueStr = ''; + $cP = 0; + foreach ($newValue as $newValuePart) { + if ($cP < 2) $newValueStr .= '-' . $newValuePart; + else $newValueStr = $newValuePart . $newValueStr; + $cP++; + } + array_push($fieldsNewValues, $newValueStr); + } + else array_push($fieldsNewValues, $newValue); + } + else array_push($fieldsNewValues, $this->data['User']['password']); + } + // compare + $fieldsResultStr = ''; + $c = 0; + foreach ($fields as $field) { + if (isset($fieldsOldValues[$c]) && $fieldsOldValues[$c] != $fieldsNewValues[$c]) { + if($field != 'confirm_password') $fieldsResultStr = $fieldsResultStr . ', ' . $field . ' (' . $fieldsOldValues[$c] . ') => (' . $fieldsNewValues[$c] . ')'; + } + $c++; + } + $fieldsResultStr = substr($fieldsResultStr, 2); + $this->extraLog("edit", "user", $fieldsResultStr); // 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')); @@ -206,6 +251,13 @@ class UsersController extends AppController { $this->request->data = $this->User->data; } + // TODO ACL CLEANUP combobox for orgs + $orgIds = array('ADMIN', 'NCIRC', 'Other MOD'); + $orgIds = $this->_arrayToValuesIndexArray($orgIds); + $this->set('orgIds', compact('orgIds')); + // XXX ACL, Groups in Users + $groups = $this->User->Group->find('list'); + $this->set(compact('groups')); } /** @@ -234,6 +286,7 @@ class UsersController extends AppController { public function login() { 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 authError before first login attempt @@ -253,7 +306,7 @@ class UsersController extends AppController { } // News page - $newNewsdate = new DateTime("2012-03-27"); + $newNewsdate = new DateTime("2012-03-27"); // TODO general, fixed odd date?? $newsdate = new DateTime($this->Auth->user('newsread')); if ($newNewsdate > $newsdate) { $this->redirect(array('action' => 'news')); @@ -264,6 +317,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()); } @@ -360,4 +414,65 @@ class UsersController extends AppController { $this->_refreshAuth(); // refresh auth info } + public function extraLog($action = null, $description = null, $fieldsResult = null) { // TODO move audit to AuditsController? + // new data + $userId = $this->Auth->user('id'); + $model = 'User'; + $modelId = $this->Auth->user('id'); + if ($action == 'login') { + $description = "User (" . $this->Auth->user('id') . "): " . $this->data['User']['email']; + } elseif ($action == 'logout') { + $description = "User (" . $this->Auth->user('id') . "): " . $this->Auth->user('email'); + } else { // edit + $description = "User (" . $this->User->id . "): " . $this->data['User']['email']; + } + + // query + $this->Log = ClassRegistry::init('Log'); + $this->Log->create(); + $this->Log->save(array( + 'org' => $this->Auth->user('org'), + 'email' => $this->Auth->user('email'), + 'action' => $action, + 'title' => $description, + 'change' => $fieldsResult)); + + // write to syslogd as well + App::import('Lib', 'SysLog.SysLog'); + $syslog = new SysLog(); + if ($fieldsResult) $syslog->write('notice', $description . ' -- ' . $action . ' -- ' . $fieldsResult); + else $syslog->write('notice', $description . ' -- ' . $action); + } + +/** + * Used for fields_before and fields for audit + * + * @param $array + */ + 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/MYSQL.correlation.sql b/app/MYSQL.correlation.sql index cb5e42243..355bf8cbf 100755 --- a/app/MYSQL.correlation.sql +++ b/app/MYSQL.correlation.sql @@ -5,6 +5,11 @@ CREATE TABLE `correlations` ( `1_attribute_id` int(11) NOT NULL, `event_id` int(11) NOT NULL, `attribute_id` int(11) NOT NULL, - `date` date NOT NULL, + `org` varchar(255) COLLATE utf8_bin NOT NULL, + `private` tinyint(1) NOT NULL, + `date` date NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=118 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; + +-- ALTER TABLE `correlations` ADD private tinyint(1) NOT NULL; +-- ALTER TABLE `correlations` ADD org varchar(255) COLLATE utf8_bin NOT NULL; \ No newline at end of file diff --git a/app/MYSQL.private.sql b/app/MYSQL.private.sql new file mode 100644 index 000000000..2a2ccb228 --- /dev/null +++ b/app/MYSQL.private.sql @@ -0,0 +1,8 @@ +ALTER TABLE `events` ADD `cluster` tinyint(1) NOT NULL; +ALTER TABLE `attributes` ADD `cluster` tinyint(1) NOT NULL; + +ALTER TABLE `events` ADD `pull` tinyint(1) NOT NULL; +ALTER TABLE `attributes` ADD `pull` tinyint(1) NOT NULL; + +ALTER TABLE `correlations` ADD private tinyint(1) NOT NULL; +ALTER TABLE `correlations` ADD org varchar(255) COLLATE utf8_bin NOT NULL; \ No newline at end of file diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 94e5e5f39..d126ce95b 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -21,6 +21,7 @@ */ App::uses('Model', 'Model'); +App::uses('LogableBehavior', 'Assets.models/behaviors'); /** * Application model for Cake. diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index a3ce44cb0..b045aa315 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -1,4 +1,5 @@ array( // TODO Audit, logable + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full' + )); + /** * Display field * @@ -17,24 +28,30 @@ class Attribute extends AppModel { */ public $displayField = 'value'; +/** + * Virtual field + * + * @var array + */ public $virtualFields = array( 'value' => 'IF (Attribute.value2="", Attribute.value1, CONCAT(Attribute.value1, "|", Attribute.value2))', 'category_order' => 'IF (Attribute.category="Internal reference", "a", -IF (Attribute.category="Antivirus detection", "b", -IF (Attribute.category="Payload delivery", "c", -IF (Attribute.category="Payload installation", "d", -IF (Attribute.category="Artifacts dropped", "e", -IF (Attribute.category="Persistence mechanism", "f", -IF (Attribute.category="Network activity", "g", -IF (Attribute.category="Payload type", "h", -IF (Attribute.category="Attribution", "i", -IF (Attribute.category="External analysis", "j", "k"))))))))))'); // TODO hardcoded + IF (Attribute.category="Antivirus detection", "b", + IF (Attribute.category="Payload delivery", "c", + IF (Attribute.category="Payload installation", "d", + IF (Attribute.category="Artifacts dropped", "e", + IF (Attribute.category="Persistence mechanism", "f", + IF (Attribute.category="Network activity", "g", + IF (Attribute.category="Payload type", "h", + IF (Attribute.category="Attribution", "i", + IF (Attribute.category="External analysis", "j", "k"))))))))))' + ); // TODO hardcoded /** - * Description field + * Field Descriptions * explanations of certain fields to be used in various views * - * @var array + * @public array */ public $fieldDescriptions = array( 'signature' => array('desc' => 'Is this attribute eligible to automatically create an IDS signature (network IDS or host IDS) out of it ?'), @@ -143,7 +160,7 @@ IF (Attribute.category="External analysis", "j", "k"))))))))))'); // TODO hardc ) ); - public $order = array("Attribute.event_id" => "DESC", "Attribute.type" => "ASC"); + public $order = array("Attribute.event_id" => "DESC", "Attribute.type" => "ASC"); /** * Validation rules @@ -257,6 +274,52 @@ IF (Attribute.category="External analysis", "j", "k"))))))))))'); // TODO hardc ), ); + public function __construct($id = false, $table = null, $ds = null) { + parent::__construct($id, $table, $ds); + + if ('true' == Configure::read('CyDefSIG.private')) { + + $this->virtualFields = Set::merge($this->virtualFields,array( + 'sharing' => 'IF (Attribute.private=true, "Org", IF (Attribute.cluster=true, "Server", IF (Attribute.pull=true, "Pull only", "All")))', + )); + + $this->fieldDescriptions = Set::merge($this->fieldDescriptions,array( + 'sharing' => array('desc' => 'This field tells how and if the attribute should be shared with other CyDefSIG users'), + )); + + $this->validate = Set::merge($this->validate,array( + 'cluster' => array( + 'boolean' => array( + 'rule' => array('boolean'), + //'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 + ), + ), + 'pull' => array( + 'boolean' => array( + 'rule' => array('boolean'), + //'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 + ), + ), + 'sharing' => array( + 'rule' => array('inList', array('Org', 'Server', 'Pull only')), + //'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 /** @@ -345,6 +408,32 @@ IF (Attribute.category="External analysis", "j", "k"))))))))))'); // TODO hardc } } + public function massageData(&$data) { + switch ($data['Attribute']['sharing']) { + case 'Org': + $data['Attribute']['private'] = true; + $data['Attribute']['cluster'] = false; + $data['Attribute']['pull'] = false; + break; + case 'Server': + $data['Attribute']['private'] = false; + $data['Attribute']['cluster'] = true; + $data['Attribute']['pull'] = false; + break; + case 'Pull only': + $data['Attribute']['private'] = false; + $data['Attribute']['cluster'] = false; + $data['Attribute']['pull'] = true; + break; + case 'All': + $data['Attribute']['private'] = false; + $data['Attribute']['cluster'] = false; + $data['Attribute']['pull'] = false; + break; + } + return $data; + } + public function beforeValidate() { // remove leading and trailing blanks $this->data['Attribute']['value'] = trim($this->data['Attribute']['value']); @@ -682,10 +771,70 @@ IF (Attribute.category="External analysis", "j", "k"))))))))))'); // TODO hardc } } +/** + * add_attachment method + * + * @return void + */ + public function uploadAttachment($fileP, $realFileName, $malware, $eventId = null, $category = null, $extraPath = '', $fullFileName = '') { + // Check if there were problems with the file upload + // only keep the last part of the filename, this should prevent directory attacks + $filename = basename($fileP); + $tmpfile = new File($fileP); + + // save the file-info in the database + $this->create(); + $this->data['Attribute']['event_id'] = $eventId; + if ($malware) { + $this->data['Attribute']['category'] = $category ? $category : "Payload delivery"; + $this->data['Attribute']['type'] = "malware-sample"; + $this->data['Attribute']['value'] = $fullFileName ? $fullFileName . '|' . $tmpfile->md5() : $filename . '|' . $tmpfile->md5(); // TODO gives problems with bigger files + $this->data['Attribute']['to_ids'] = 1; // LATER let user choose to send this to IDS + } else { + $this->data['Attribute']['category'] = $category ? $category : "Artifacts dropped"; + $this->data['Attribute']['type'] = "attachment"; + $this->data['Attribute']['value'] = $fullFileName ? $fullFileName : $realFileName; + $this->data['Attribute']['to_ids'] = 0; + } + + if ($this->save($this->data)) { + // attribute saved correctly in the db + } else { + // do some? + } + + // no errors in file upload, entry already in db, now move the file where needed and zip it if required. + // no sanitization is required on the filename, path or type as we save + // create directory structure + $rootDir = APP . DS . "files" . DS . $eventId; + $dir = new Folder($rootDir, true); + // move the file to the correct location + $destpath = $rootDir . DS . $this->getId(); // id of the new attribute in the database + $file = new File ($destpath); + $zipfile = new File ($destpath . '.zip'); + $fileInZip = new File($rootDir . DS . $extraPath . $filename); // FIXME do sanitization of the filename + + // zip and password protect the malware files + if ($malware) { + // TODO check if CakePHP has no easy/safe wrapper to execute commands + $execRetval = ''; + $execOutput = array(); + exec("zip -j -P infected " . $zipfile->path . ' "' . addslashes($fileInZip->path) . '"', $execOutput, $execRetval); + if ($execRetval != 0) { // not EXIT_SUCCESS + // do some? + }; + $fileInZip->delete(); // delete the original not-zipped-file + rename($zipfile->path, $file->path); // rename the .zip to .nothing + } else { + $fileAttach = new File($fileP); + rename($fileAttach->path, $file->path); + } + } + private function __afterSaveCorrelation($attribute) { $this->__beforeDeleteCorrelation($attribute); // re-add - $this->setRelatedAttributes($attribute, array('Attribute.id', 'Attribute.event_id', 'Event.date')); + $this->setRelatedAttributes($attribute, array('Attribute.id', 'Attribute.event_id', 'Attribute.private', 'Event.date', 'Event.org')); } private function __beforeDeleteCorrelation($attribute) { @@ -741,7 +890,7 @@ IF (Attribute.category="External analysis", "j", "k"))))))))))'); // TODO hardc $params = array( 'conditions' => array('Event.id' => $relatedAttribute['Attribute']['event_id']), 'recursive' => 0, - 'fields' => array('Event.date') + 'fields' => array('Event.date', 'Event.org') ); $eventDate = $this->Event->find('first', $params); $this->Correlation = ClassRegistry::init('Correlation'); @@ -750,6 +899,8 @@ IF (Attribute.category="External analysis", "j", "k"))))))))))'); // TODO hardc 'Correlation' => array( '1_event_id' => $attribute['event_id'], '1_attribute_id' => $attribute['id'], 'event_id' => $relatedAttribute['Attribute']['event_id'], 'attribute_id' => $relatedAttribute['Attribute']['id'], + 'org' => $eventDate['Event']['org'], + 'private' => $relatedAttribute['Attribute']['private'], 'date' => $eventDate['Event']['date'])) ); } diff --git a/app/Model/Event.php b/app/Model/Event.php old mode 100755 new mode 100644 index 25b5bb0dc..801825d72 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -8,6 +8,14 @@ App::uses('AppModel', 'Model'); */ class Event extends AppModel { + public $name = 'Event'; // TODO general + + public $actsAs = array('SysLogLogable.SysLogLogable' => array( // TODO Audit, logable + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full' + )); + /** * Display field * @@ -15,6 +23,8 @@ class Event extends AppModel { */ public $displayField = 'id'; + public $virtualFields = array(); + /** * Description field * @@ -23,7 +33,8 @@ class Event extends AppModel { public $fieldDescriptions = array( 'risk' => array('desc' => 'Risk levels: *low* means mass-malware, *medium* means APT malware, *high* means sophisticated APT malware or 0-day attack', 'formdesc' => 'Risk levels:
low: mass-malware
medium: APT malware
high: sophisticated APT malware or 0-day attack'), 'private' => array('desc' => 'This field tells if the event should be shared with other CyDefSIG servers'), - 'classification' => array('desc' => 'Set the Traffic Light Protocol classification.
  1. TLP:AMBER- Share only within the organization on a need-to-know basis
  2. TLP:GREEN:NeedToKnow- Share within your constituency on the need-to-know basis.
  3. TLP:GREEN- Share within your constituency.
') + 'classification' => array('desc' => 'Set the Traffic Light Protocol classification.
  1. TLP:AMBER- Share only within the organization on a need-to-know basis
  2. TLP:GREEN:NeedToKnow- Share within your constituency on the need-to-know basis.
  3. TLP:GREEN- Share within your constituency.
'), + 'submittedfile' => array('desc' => 'GFI sandbox: export upload', 'formdesc' => 'GFI sandbox:
export upload'), ); /** @@ -80,6 +91,16 @@ class Event extends AppModel { //'on' => 'create', // Limit validation to 'create' or 'update' operations ), ), + 'user_id' => array( + 'numeric' => array( + 'rule' => array('numeric'), + //'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 + ), + ), 'published' => array( 'boolean' => array( 'rule' => array('boolean'), @@ -120,6 +141,52 @@ class Event extends AppModel { //), ); + public function __construct($id = false, $table = null, $ds = null) { + parent::__construct($id, $table, $ds); + + if ('true' == Configure::read('CyDefSIG.private')) { + + $this->virtualFields = Set::merge($this->virtualFields,array( + 'sharing' => 'IF (Event.private=true, "Org", IF (Event.cluster=true, "Server", IF (Event.pull=true, "Pull only", "All")))', + )); + + $this->fieldDescriptions = Set::merge($this->fieldDescriptions,array( + 'sharing' => array('desc' => 'This field tells how and if the event should be shared with other CyDefSIG users'), + )); + + $this->validate = Set::merge($this->validate,array( + 'cluster' => array( + 'boolean' => array( + 'rule' => array('boolean'), + //'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 + ), + ), + 'pull' => array( + 'boolean' => array( + 'rule' => array('boolean'), + //'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 + ), + ), + 'sharing' => array( + 'rule' => array('inList', array('Org', 'Server', 'Pull only')), + //'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 /** @@ -165,13 +232,67 @@ class Event extends AppModel { ) ); + public function beforeDelete() { + // delete event from the disk + $this->read(); // first read the event from the db + // FIXME secure this filesystem access/delete by not allowing to change directories or go outside of the directory container. + // only delete the file if it exists + $filepath = APP . "files" . DS . $this->data['Event']['id']; + App::uses('Folder', 'Utility'); + $file = new Folder ($filepath); + if (is_dir($filepath)) { + if (!$this->destroyDir($filepath)) { + throw new InternalErrorException('Delete of event file directory failed. Please report to administrator.'); + } + } + } + + public function destroyDir($dir) { + if (!is_dir($dir) || is_link($dir)) return unlink($dir); + foreach (scandir($dir) as $file) { + if ($file == '.' || $file == '..') continue; + if (!$this->destroyDir($dir . DS . $file)) { + chmod($dir . DS . $file, 0777); + if (!$this->destroyDir($dir . DS . $file)) return false; + }; + } + return rmdir($dir); + } + public function beforeValidate() { + parent::beforeValidate(); // generate UUID if it doesn't exist if (empty($this->data['Event']['uuid'])) { $this->data['Event']['uuid'] = String::uuid(); } } + public function massageData(&$data) { + switch ($data['Event']['sharing']) { + case 'Org': + $data['Event']['private'] = true; + $data['Event']['cluster'] = false; + $data['Event']['pull'] = false; + break; + case 'Server': + $data['Event']['private'] = false; + $data['Event']['cluster'] = true; + $data['Event']['pull'] = false; + break; + case 'Pull only': + $data['Event']['private'] = false; + $data['Event']['cluster'] = false; + $data['Event']['pull'] = true; + break; + case 'All': + $data['Event']['private'] = false; + $data['Event']['cluster'] = false; + $data['Event']['pull'] = false; + break; + } + return $data; + } + public function isOwnedByOrg($eventid, $org) { return $this->field('id', array('id' => $eventid, 'org' => $org)) === $eventid; } @@ -239,9 +360,12 @@ class Event extends AppModel { * @return bool true if success, error message if failed */ public function uploadEventToServer($event, $server, $HttpSocket=null) { - if (true == $event['Event']['private']) { // never upload private events + if (('true' != Configure::read('CyDefSIG.private')) && (true == $event['Event']['private'])) { // never upload private events return "Event is private and non exportable"; } + if (('true' == Configure::read('CyDefSIG.private')) && (true == $event['Event']['pull'])) { + return "Event is pull only and non exportable"; + } $url = $server['Server']['url']; $authkey = $server['Server']['authkey']; diff --git a/app/Model/Group.php b/app/Model/Group.php new file mode 100755 index 000000000..0cf91d66f --- /dev/null +++ b/app/Model/Group.php @@ -0,0 +1,62 @@ + 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 + ), + ), + ); + +/** + * 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 + * + * @var unknown_type + */ + public $actsAs = array('Acl' => array('type' => 'requester'), 'MagicTools.OrphansProtectable'); + +/** + * TODO ACL: 2: hook Group into CakePHP ACL system (so link to aros) + */ + public function parentNode() { + return null; + } +} \ No newline at end of file diff --git a/app/Model/Log.php b/app/Model/Log.php new file mode 100755 index 000000000..168ec5e90 --- /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 $actionDefinitions = 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 b16b6f7d9..29af76cd8 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -6,6 +6,14 @@ App::uses('AppModel', 'Model'); */ class Server extends AppModel { + public $name = 'Server'; // TODO general + + public $actsAs = array('SysLogLogable.SysLogLogable' => array( // TODO Audit, logable, check: 'userModel' and 'userKey' can be removed given default + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full' + )); + /** * Display field * @@ -94,4 +102,4 @@ class Server extends AppModel { public function isOwnedByOrg($serverid, $org) { return $this->field('id', array('id' => $serverid, 'org' => $org)) === $serverid; } -} \ No newline at end of file +} diff --git a/app/Model/User.php b/app/Model/User.php index 3956d8b7b..39ea0b654 100755 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -17,6 +17,14 @@ class User extends AppModel { */ public $displayField = 'email'; + public $orgField = 'org'; // TODO Audit, LogableBehaviour + org +/** + * Model Name + * + * @var string + */ + public $name = 'User'; // TODO general + /** * Validation rules * @@ -69,6 +77,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'), @@ -162,6 +180,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 * @@ -183,6 +216,38 @@ 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 + */ + public 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/MagicTools/Model/Behavior/OrphansProtectableBehavior.php b/app/Plugin/MagicTools/Model/Behavior/OrphansProtectableBehavior.php new file mode 100644 index 000000000..399c935b1 --- /dev/null +++ b/app/Plugin/MagicTools/Model/Behavior/OrphansProtectableBehavior.php @@ -0,0 +1,125 @@ +_deletionError = null; // Stores the deletion error message + $Model->orphansProtectableOptions = array_merge(array( + 'listPossibleOrphans' => true, + 'htmlError' => false + ), $settings); + } + + /** + * Checks if there would be orphaned record left behind after deletion of this record; if so, deletion is prevented. + * + * @param $model Model + * @param $cascade boolean + * @return boolean + */ + function beforeDelete(&$Model, $cascade) { + if($cascade) return true; + return !$Model->wouldLeaveOrphanedRecordsBehind(); + } + + /** + * Checks if deletion of this record would leave orphaned associated records behind. + * + * @param $model Model + * @return boolean + */ + function wouldLeaveOrphanedRecordsBehind(&$Model) { + $possibleOrphans = array(); + + foreach($Model->hasMany as $model => $settings) { + // Is relationship is dependent? + if($settings['dependent']){ // Yes! Possible orphans are deleted, too! + // Do nothing + } else { // No! Possible orphans should be protected! + // Is there a possible orphan for this relation? + $Model->{$model}->recursive = -1; + $objects = $Model->{$model}->find('all', array('conditions' => array($settings['className'].'.'.$settings['foreignKey'] => $Model->id), 'order' => 'id asc')); + if(count($objects) > 0) { // Yes, there is at least one possible orphan! + $objectIds = array(); + foreach($objects as $object) { + $objectIds[] = $object[$model]['id']; + } + $possibleOrphans[$model] = $objectIds; + } + } + } + + // Would orphans be left behind? + if(count($possibleOrphans) > 0) { // Yes! Create deletion error message! + $Model->_deletionError = $Model->createDeletionError($possibleOrphans); + return true; + } else { // No! + return false; + } + } + + /** + * Returns the deletion error message (if there is one). + * + * @param $model Model + * @return string + */ + function getDeletionError(&$Model) { + return $Model->_deletionError; + } + + /** + * Creates the deletion error message and returns it. + * + * @param $model Model + * @param $possibleOrphans array + * @return string + */ + function createDeletionError(&$Model, $possibleOrphans) { + $errorParts = array(); + foreach($possibleOrphans as $model => $ids) { + $count = count($ids); + $modelName = $count > 1 ? Inflector::pluralize($model) : $model; + $errorParts[] = $count.' '.__($modelName, true).' (ID: '.$Model->createDeletionErrorIds($model, $ids).')'; + } + return __('it has the following dependent items', true).': '.implode($errorParts, ', '); + } + + /** + * Creates a string containing HTML-links to comma separated IDs of the potentially orphaned records of the specified model. + * + * @param $model Model + * @param $orphanModel string + * @param $ids array + * @return string + */ + function createDeletionErrorIds(&$Model, $orphanModel, $ids) { + $messageParts = array(); + if($Model->orphansProtectableOptions['htmlError']) { + foreach($ids as $id) { + $messageParts[] = ''.$id.''; // TODO: Noch unschön! --zivi-muh + } + } else { + $messageParts = $ids; + } + return implode($messageParts, ', '); + } +} +?> \ No newline at end of file diff --git a/app/Plugin/SysLogLogable/Model/Behavior/SysLogLogableBehavior.php b/app/Plugin/SysLogLogable/Model/Behavior/SysLogLogableBehavior.php new file mode 100644 index 000000000..560b5f9ee --- /dev/null +++ b/app/Plugin/SysLogLogable/Model/Behavior/SysLogLogableBehavior.php @@ -0,0 +1,220 @@ +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); + } + + 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": + $this->Events = new EventsController(); + $this->Events->constructClasses(); + $title = 'Event ('. $Model->data[$Model->alias]['id'].'): '.$this->Events->getName($Model->data[$Model->alias]['id']); + $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": + $this->Servers = new ServersController(); + $this->Servers->constructClasses(); + $title = 'Server ('. $Model->data[$Model->alias]['id'].'): '. $this->Servers->getName($Model->data[$Model->alias]['id']); + $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(); + if (isset($logData['Log']['change'])) { + $syslog->write('notice', $logData['Log']['description'].' -- '.$logData['Log']['change']); + } else { + $syslog->write('notice', $logData['Log']['description']); + } + } +} \ No newline at end of file diff --git a/app/README.txt b/app/README.txt index 4e24c94ee..9bc174af2 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 pear install Net_GeoIP # ideally make sure geoip database is updated using crontab @@ -80,6 +91,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. 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/add.ctp b/app/View/Attributes/add.ctp index 8ac677f89..782093bec 100755 --- a/app/View/Attributes/add.ctp +++ b/app/View/Attributes/add.ctp @@ -14,9 +14,15 @@ echo $this->Form->input('type', array( 'empty' => '(first choose category)' )); if ('true' == Configure::read('CyDefSIG.sync')) { - echo $this->Form->input('private', array( - 'before' => $this->Html->div('forminfo', isset($attrDescriptions['private']['formdesc']) ? $attrDescriptions['private']['formdesc'] : $attrDescriptions['private']['desc']), - )); + if ('true' == Configure::read('CyDefSIG.private')) { + echo $this->Form->input('sharing', array('label' => 'Private', + 'before' => $this->Html->div('forminfo', isset($attrDescriptions['sharing']['formdesc']) ? $attrDescriptions['sharing']['formdesc'] : $attrDescriptions['sharing']['desc']), + )); + } else { + echo $this->Form->input('private', array( + 'before' => $this->Html->div('forminfo', isset($attrDescriptions['private']['formdesc']) ? $attrDescriptions['private']['formdesc'] : $attrDescriptions['private']['desc']), + )); + } } echo $this->Form->input('to_ids', array( 'checked' => true, diff --git a/app/View/Attributes/add_attachment.ctp b/app/View/Attributes/add_attachment.ctp index affc7a2bc..e729e4dbd 100755 --- a/app/View/Attributes/add_attachment.ctp +++ b/app/View/Attributes/add_attachment.ctp @@ -14,8 +14,13 @@ echo $this->Form->input('malware', array( 'after' => '
Tick this box to neutralize the sample. Every malware sample will be zipped with the password "infected"', )); if ('true' == Configure::read('CyDefSIG.sync')) { - echo $this->Form->input('private', array( + if ('true' == Configure::read('CyDefSIG.private')) { + echo $this->Form->input('sharing', array('label' => 'Private', + 'before' => $this->Html->div('forminfo', isset($attrDescriptions['sharing']['formdesc']) ? $attrDescriptions['sharing']['formdesc'] : $attrDescriptions['sharing']['desc']),)); + } else { + echo $this->Form->input('private', array( 'before' => $this->Html->div('forminfo', isset($attrDescriptions['private']['formdesc']) ? $attrDescriptions['private']['formdesc'] : $attrDescriptions['private']['desc']),)); + } } // link an onchange event to the form elements $this->Js->get('#AttributeType')->event('change', 'showFormInfo("#AttributeType")'); diff --git a/app/View/Attributes/edit.ctp b/app/View/Attributes/edit.ctp index 7af48bcef..f291de012 100755 --- a/app/View/Attributes/edit.ctp +++ b/app/View/Attributes/edit.ctp @@ -1,3 +1,7 @@ +
Form->create('Attribute');?>
@@ -12,9 +16,15 @@ if ($attachment) { echo $this->Form->input('type', array('between' => $this->Html->div('forminfo', '', array('id' => 'AttributeTypeDiv')))); } if ('true' == Configure::read('CyDefSIG.sync')) { - echo $this->Form->input('private', array( - 'before' => $this->Html->div('forminfo', isset($attrDescriptions['private']['formdesc']) ? $attrDescriptions['private']['formdesc'] : $attrDescriptions['private']['desc']), - )); + if ('true' == Configure::read('CyDefSIG.private')) { + echo $this->Form->input('sharing', array('label' => 'Private', + 'before' => $this->Html->div('forminfo', isset($attrDescriptions['sharing']['formdesc']) ? $attrDescriptions['sharing']['formdesc'] : $attrDescriptions['sharing']['desc']), + )); + } else { + echo $this->Form->input('private', array( + 'before' => $this->Html->div('forminfo', isset($attrDescriptions['private']['formdesc']) ? $attrDescriptions['private']['formdesc'] : $attrDescriptions['private']['desc']), + )); + } } echo $this->Form->input('to_ids', array( 'before' => $this->Html->div('forminfo', isset($attrDescriptions['signature']['formdesc']) ? $attrDescriptions['signature']['formdesc'] : $attrDescriptions['signature']['desc']), @@ -38,7 +48,10 @@ $this->Js->get('#AttributeType')->event('change', 'showFormInfo("#AttributeType"
@@ -95,8 +108,8 @@ function showFormInfo(id) { // LATER use nice animations //$(idDiv).hide('fast'); // change the content - var value = $(id).val(); // get the selected value - $(idDiv).html(formInfoValues[value]); // search in a lookup table + var value = $(id).val(); // get the selected value + $(idDiv).html(formInfoValues[value]); // search in a lookup table // show it again $(idDiv).fadeIn('slow'); @@ -111,4 +124,12 @@ formCategoryChanged("#AttributeCategory"); $('#AttributeType').val(type_value); -Js->writeBuffer(); // Write cached scripts \ No newline at end of file +Js->writeBuffer(); // Write cached scripts ?> + + + +Js->writeBuffer(); // Write cached scripts diff --git a/app/View/Attributes/index.ctp b/app/View/Attributes/index.ctp index 3d2c67a4a..26699d622 100755 --- a/app/View/Attributes/index.ctp +++ b/app/View/Attributes/index.ctp @@ -1,3 +1,7 @@ +

@@ -37,8 +41,9 @@ if ('attachment' == $attribute['Attribute']['type'] || 'malware-sample' == $attr  
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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); + 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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); } echo $this->Html->link(__('View'), array('controller' => 'events', 'action' => 'view', $attribute['Attribute']['event_id'])); ?> @@ -65,4 +70,401 @@ echo $this->Html->link(__('View'), array('controller' => 'events', 'action' => '
    element('actions_menu'); ?>
- \ No newline at end of file + + + + \ No newline at end of file diff --git a/app/View/Attributes/xml/index.ctp b/app/View/Attributes/xml/index.ctp index 249d8d9aa..257402f4c 100755 --- a/app/View/Attributes/xml/index.ctp +++ b/app/View/Attributes/xml/index.ctp @@ -10,6 +10,10 @@ foreach ($attributes as $key => $attribute) { if ('true' != Configure::read('CyDefSIG.sync')) { unset($attributes[$key]['private']); } + if ('true' == Configure::read('CyDefSIG.private')) { + unset($attributes[$key]['sharing']); + unset($attributes[$key]['cluster']); + } } diff --git a/app/View/Elements/actions_menu.ctp b/app/View/Elements/actions_menu.ctp index d29b69ee8..727c845ba 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' => $buttonAddStatus,'class' => $buttonAddStatus)); ?>
  • 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')); ?>
  • @@ -25,4 +26,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)); ?>
  • -Form->create('Event');?> +Form->create('Event', array('type' => 'file'));?>
    Form->input('date'); if ('true' == Configure::read('CyDefSIG.sync')) { - echo $this->Form->input('private', array( - 'before' => $this->Html->div('forminfo', isset($eventDescriptions['private']['formdesc']) ? $eventDescriptions['private']['formdesc'] : $eventDescriptions['private']['desc']),)); + if ('true' == Configure::read('CyDefSIG.private')) { + echo $this->Form->input('sharing', array('label' => 'Private', + 'before' => $this->Html->div('forminfo', isset($eventDescriptions['sharing']['formdesc']) ? $eventDescriptions['sharing']['formdesc'] : $eventDescriptions['sharing']['desc']),)); + } else { + echo $this->Form->input('private', array( + 'before' => $this->Html->div('forminfo', isset($eventDescriptions['private']['formdesc']) ? $eventDescriptions['private']['formdesc'] : $eventDescriptions['private']['desc']),)); + } } echo $this->Form->input('risk', array( 'before' => $this->Html->div('forminfo', isset($eventDescriptions['risk']['formdesc']) ? $eventDescriptions['risk']['formdesc'] : $eventDescriptions['risk']['desc']))); echo $this->Form->input('info'); +echo $this->Form->input('Event.submittedfile', array( + 'label' => 'GFI sandbox', + 'between' => '
    ', + 'type' => 'file', + 'before' => $this->Html->div('forminfo', isset($eventDescriptions['submittedfile']['formdesc']) ? $eventDescriptions['submittedfile']['formdesc'] : $eventDescriptions['submittedfile']['desc']))); ?>
    @@ -21,4 +31,4 @@ echo $this->Form->input('info'); element('actions_menu'); ?> - \ No newline at end of file + diff --git a/app/View/Events/edit.ctp b/app/View/Events/edit.ctp index ff4654001..ebe34e7d8 100755 --- a/app/View/Events/edit.ctp +++ b/app/View/Events/edit.ctp @@ -8,8 +8,13 @@ echo $this->Form->input('date'); echo $this->Form->input('risk', array( 'before' => $this->Html->div('forminfo', isset($eventDescriptions['risk']['formdesc']) ? $eventDescriptions['risk']['formdesc'] : $eventDescriptions['risk']['desc']))); if ('true' == Configure::read('CyDefSIG.sync')) { - echo $this->Form->input('private', array( - 'before' => $this->Html->div('forminfo', isset($eventDescriptions['private']['formdesc']) ? $eventDescriptions['private']['formdesc'] : $eventDescriptions['private']['desc']),)); + if ('true' == Configure::read('CyDefSIG.private')) { + echo $this->Form->input('sharing', array('label' => 'Private', + 'before' => $this->Html->div('forminfo', isset($eventDescriptions['sharing']['formdesc']) ? $eventDescriptions['sharing']['formdesc'] : $eventDescriptions['sharing']['desc']),)); + } else { + echo $this->Form->input('private', array( + 'before' => $this->Html->div('forminfo', isset($eventDescriptions['private']['formdesc']) ? $eventDescriptions['private']['formdesc'] : $eventDescriptions['private']['desc']),)); + } } echo $this->Form->input('info'); ?> diff --git a/app/View/Events/index.ctp b/app/View/Events/index.ctp index a9992fcad..cf578a909 100755 --- a/app/View/Events/index.ctp +++ b/app/View/Events/index.ctp @@ -1,3 +1,9 @@ +

    Events

    @@ -51,13 +57,15 @@
    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' => $buttonPublishStatus . $buttonCounter++, 'class' => $buttonPublishStatus, 'action' => 'alert', $event['Event']['id']), array('id' => $buttonPublishStatus . $buttonCounter++, 'class' => $buttonPublishStatus, '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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); + 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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); } ?> Html->link(__('View', true), array('controller' => 'events', 'action' => 'view', $event['Event']['id'])); ?> @@ -85,3 +93,580 @@ if ($isAdmin || $event['Event']['org'] == $me['org']) { element('actions_menu'); ?> + + + diff --git a/app/View/Events/view.ctp b/app/View/Events/view.ctp index 855e71873..d618c5e26 100755 --- a/app/View/Events/view.ctp +++ b/app/View/Events/view.ctp @@ -1,11 +1,24 @@ +
    • 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' => $buttonPublishStatus . $buttonCounter++, 'class' => $buttonPublishStatus)); + echo $this->Html->link('Publish (no email)', array('action' => 'publish', $event['Event']['id']), array('id' => $buttonPublishStatus . $buttonCounter++, 'class' => $buttonPublishStatus)); +} ?>
    • Not published
    @@ -50,12 +63,20 @@   + +
    Private
    +
    + +   +
    +
    Private
     
    + + + diff --git a/app/View/Events/xml/index.ctp b/app/View/Events/xml/index.ctp index e654a5f8c..4191d6bc5 100644 --- a/app/View/Events/xml/index.ctp +++ b/app/View/Events/xml/index.ctp @@ -11,6 +11,10 @@ foreach ($events as $key => $event) { if ('true' != Configure::read('CyDefSIG.sync')) { unset($events[$key]['private']); } + if ('true' == Configure::read('CyDefSIG.private')) { + unset($events[$key]['cluster']); + unset($events[$key]['sharing']); + } // hide the org field is we are not in showorg mode if ('true' != Configure::read('CyDefSIG.showorg') && !$isAdmin) { unset($events[$key]['org']); diff --git a/app/View/Events/xml/view.ctp b/app/View/Events/xml/view.ctp index c34c5a433..4f24de28e 100755 --- a/app/View/Events/xml/view.ctp +++ b/app/View/Events/xml/view.ctp @@ -9,8 +9,18 @@ unset($event['Attribute']); foreach ($event['Event']['Attribute'] as $key => $value) { unset($event['Event']['Attribute'][$key]['value1']); unset($event['Event']['Attribute'][$key]['value2']); + unset($event['Event']['Attribute'][$key]['category_order']); } +// hide the share fields is we are not in private mode +if ('true' == Configure::read('CyDefSIG.private')) { + unset($event['Event']['cluster']); + unset($event['Event']['sharing']); + foreach ($event['Event']['Attribute'] as $key => $value) { + unset($event['Event']['Attribute'][$key]['cluster']); + unset($event['Event']['Attribute'][$key]['sharing']); + } +} // hide the private fields is we are not in sync mode if ('true' != Configure::read('CyDefSIG.sync')) { unset($event['Event']['private']); 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..517d280f3 --- /dev/null +++ b/app/View/Groups/admin_index.ctp @@ -0,0 +1,88 @@ +
    +

    + + + + + + + + + + + + + + + + + + + + + +
    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'); ?> +
    +
    + + \ No newline at end of file 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 2a2e4ffcc..39c8378ab --- a/app/View/Layouts/default.ctp +++ b/app/View/Layouts/default.ctp @@ -37,6 +37,8 @@ echo $this->Html->script('jquery-1.8.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..f0d024965 --- /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 824958e0f..1f6cab5ca --- a/app/View/Servers/index.ctp +++ b/app/View/Servers/index.ctp @@ -1,3 +1,10 @@ +

    @@ -26,8 +33,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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); ?> + 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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); ?> Form->postLink(__('Pull'), array('action' => 'pull', $server['Server']['id']) ); ?> Form->postLink(__('Push'), array('action' => 'push', $server['Server']['id']) ); ?> @@ -56,9 +64,404 @@
      -
    • Html->link(__('New Server'), array('controller' => 'servers', 'action' => 'add')); ?>
    • +
    • Html->link(__('New Server'), array('controller' => 'servers', 'action' => 'add'), array('id' => $buttonAddStatus . $buttonCounter++, 'class' => $buttonAddStatus)); ?>
    • Html->link(__('List Servers'), array('controller' => 'servers', 'action' => 'index'));?>
    •  
    • element('actions_menu'); ?>
    + diff --git a/app/View/Users/admin_add.ctp b/app/View/Users/admin_add.ctp old mode 100644 new mode 100755 index 486a4042f..0e572614d --- 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 3148e7590..81ef66733 --- 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 81411a0a5..4cdaf5c5f --- 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'); ?> + diff --git a/app/View/Users/admin_view.ctp b/app/View/Users/admin_view.ctp old mode 100644 new mode 100755 index e974a2683..78ec41ce6 --- a/app/View/Users/admin_view.ctp +++ b/app/View/Users/admin_view.ctp @@ -1,6 +1,12 @@ +
    -
    • 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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); ?>

    @@ -19,6 +25,11 @@   +
    +
    + Html->link($user['Group']['name'], array('controller' => 'groups', 'action' => 'view', $user['Group']['id'])); ?> +   +
    @@ -66,12 +77,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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); ?>
    • +
    • 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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); + ?>
    • 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' => $buttonAddStatus . $buttonCounter++, 'class' => $buttonAddStatus)); ?>
    • 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' => $buttonAddStatus . $buttonCounter++, 'class' => $buttonAddStatus)); ?>
    @@ -112,3 +129,35 @@ + \ No newline at end of file diff --git a/app/View/Users/edit.ctp b/app/View/Users/edit.ctp index b96f38bb3..8b864f41c 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'); @@ -22,4 +23,4 @@
  •  
  • element('actions_menu'); ?> - \ No newline at end of file + diff --git a/app/View/Users/news.ctp b/app/View/Users/news.ctp old mode 100644 new mode 100755 index 393f77e42..7658aa57c --- 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.
    diff --git a/app/View/Users/terms.ctp b/app/View/Users/terms.ctp index a5d742888..57547e074 100644 --- a/app/View/Users/terms.ctp +++ b/app/View/Users/terms.ctp @@ -50,3 +50,8 @@ if (!$termsaccepted) { element('actions_menu'); ?>

    + diff --git a/app/View/Users/view.ctp b/app/View/Users/view.ctp index e115a5e4e..333767746 100755 --- a/app/View/Users/view.ctp +++ b/app/View/Users/view.ctp @@ -19,6 +19,11 @@   +
    +
    + +   +
    @@ -55,3 +60,36 @@ element('actions_menu'); ?> + + \ No newline at end of file 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/tmp/cache/models/empty b/app/tmp/cache/models/empty old mode 100644 new mode 100755 diff --git a/app/tmp/cache/persistent/empty b/app/tmp/cache/persistent/empty old mode 100644 new mode 100755 diff --git a/app/tmp/cache/views/empty b/app/tmp/cache/views/empty old mode 100644 new mode 100755 diff --git a/app/tmp/logs/empty b/app/tmp/logs/empty old mode 100644 new mode 100755 diff --git a/app/tmp/sessions/empty b/app/tmp/sessions/empty old mode 100644 new mode 100755 diff --git a/app/tmp/tests/empty b/app/tmp/tests/empty old mode 100644 new mode 100755 diff --git a/app/webroot/css/cake.generic.css b/app/webroot/css/cake.generic.css old mode 100644 new mode 100755 index 4b8e1eda2..1acfb3560 --- 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; +} 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 diff --git a/plugins/AclExtras/Console/Command/AclExtrasShell.php b/plugins/AclExtras/Console/Command/AclExtrasShell.php new file mode 100644 index 000000000..7ac1a36f2 --- /dev/null +++ b/plugins/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/plugins/Assets/models/behaviors/LogableBehavior.php b/plugins/Assets/models/behaviors/LogableBehavior.php new file mode 100644 index 000000000..e17f0ba93 --- /dev/null +++ b/plugins/Assets/models/behaviors/LogableBehavior.php @@ -0,0 +1,535 @@ + (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 = ''; + } + if ($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'] .= '.'; + } + $this->Log->create($logData); + $this->Log->save(null, array( + 'validate' => false, + 'callbacks' => false)); + } +} \ No newline at end of file diff --git a/plugins/SysLog/Lib/SysLog.php b/plugins/SysLog/Lib/SysLog.php new file mode 100644 index 000000000..3116ffcd3 --- /dev/null +++ b/plugins/SysLog/Lib/SysLog.php @@ -0,0 +1,91 @@ +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/plugins/index.txt b/plugins/index.txt new file mode 100644 index 000000000..96470e1ca --- /dev/null +++ b/plugins/index.txt @@ -0,0 +1,4 @@ +https://github.com/markstory/acl_extras +https://github.com/eskil-saatvedt/CakePHP-Assets (LogableBehavior) +http://bakery.cakephp.org/articles/rikdc/2010/06/07/syslog-component +http://xp-dev.com/trac/cakephp/browser/cakephp/libs/magic_tools \ No newline at end of file diff --git a/plugins/magic_tools/models/behaviors/orphans_protectable.php b/plugins/magic_tools/models/behaviors/orphans_protectable.php new file mode 100644 index 000000000..c4e2d61f4 --- /dev/null +++ b/plugins/magic_tools/models/behaviors/orphans_protectable.php @@ -0,0 +1,126 @@ +_deletionError = null; // Stores the deletion error message + $Model->orphansProtectableOptions = array_merge(array( + 'listPossibleOrphans' => true, + 'htmlError' => false + ), $settings); + } + + /** + * Checks if there would be orphaned record left behind after deletion of this record; if so, deletion is prevented. + * + * @param $model Model + * @param $cascade boolean + * @return boolean + */ + function beforeDelete(&$model, $cascade) { + if($cascade) return true; + return !$Model->wouldLeaveOrphanedRecordsBehind(); + } + + /** + * Checks if deletion of this record would leave orphaned associated records behind. + * + * @param $model Model + * @return boolean + */ + function wouldLeaveOrphanedRecordsBehind(&$model) { + $possibleOrphans = array(); + + foreach($Model->hasMany as $model => $settings) { + // Is relationship is dependent? + if($settings['dependent']){ // Yes! Possible orphans are deleted, too! + // Do nothing + } else { // No! Possible orphans should be protected! + // Is there a possible orphan for this relation? + $Model->{$model}->recursive = -1; + $objects = $Model->{$model}->find('all', array('conditions' => array($settings['className'].'.'.$settings['foreignKey'] => $Model->id), 'order' => 'id asc')); + if(count($objects) > 0) { // Yes, there is at least one possible orphan! + $objectIds = array(); + foreach($objects as $object) { + $objectIds[] = $object[$model]['id']; + } + $possibleOrphans[$model] = $objectIds; + } + } + } + + // Would orphans be left behind? + if(count($possibleOrphans) > 0) { // Yes! Create deletion error message! + $Model->_deletionError = $Model->createDeletionError($possibleOrphans); + return true; + } else { // No! + return false; + } + } + + /** + * Returns the deletion error message (if there is one). + * + * @param $model Model + * @return string + */ + function getDeletionError(&$model) { + return $Model->_deletionError; + } + + /** + * Creates the deletion error message and returns it. + * + * @param $model Model + * @param $possibleOrphans array + * @return string + */ + function createDeletionError(&$model, $possibleOrphans) { + $errorParts = array(); + foreach($possibleOrphans as $model => $ids) { + $count = count($ids); + $modelName = $count > 1 ? Inflector::pluralize($model) : $model; + $errorParts[] = $count.' '.__($modelName, true).' (ID: '.$Model->createDeletionErrorIds($model, $ids).')'; + } + return __('it has the following dependent items', true).': '.implode($errorParts, ', '); + } + + /** + * Creates a string containing HTML-links to comma separated IDs of the potentially orphaned records of the specified model. + * + * @param $model Model + * @param $orphanModel string + * @param $ids array + * @return string + */ + function createDeletionErrorIds(&$model, $orphanModel, $ids) { + $messageParts = array(); + if($Model->orphansProtectableOptions['htmlError']) { + foreach($ids as $id) { + $messageParts[] = ''.$id.''; // TODO: Noch unschön! --zivi-muh + } + } else { + $messageParts = $ids; + } + return implode($messageParts, ', '); + } +} +?> \ No newline at end of file diff --git a/tools/curl/input/event.xml b/tools/curl/input/event.xml index ab5e7dd75..48523c8ad 100644 --- a/tools/curl/input/event.xml +++ b/tools/curl/input/event.xml @@ -1,11 +1,11 @@ 14 - NCIRC + ORG 2012-04-12 Medium - TT6666: malixioious XLS (EDIT 234..5) - 3 + info + 1 0 4f8c2c4e-00dc-42c9-83ad-76e9ff32448e 0 @@ -31,8 +31,7 @@ 4f8c2d08-7e6c-4648-8730-50a7ff32448e 1 0 - Summary_report_Vienna_2012 27 March - ok_z.doc|b34a8fcf8e5c81de3f6f177bb6171929 + A.doc|3f6f1aaab6171925c81de9b34a8fcf8e c @@ -45,22 +44,22 @@ 4f8c2c69-9bf8-4279-8d03-2138ff32448e 1 0 - CVE-2010-3333 + CVE-XXXX-XXXX c 11 - 2012-04-03 + 2011-01-03 4f8812ff-ded0-4592-9227-0615ff32448e 9 - 2012-04-02 + 2011-02-02 4f85981e-d044-4b16-bc16-0a35ff32448e 6 - 2012-03-22 + 2011-03-01 4f7a9faa-91d4-4c91-8ec6-0878ff32448e
    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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); ?> + 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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); + ?>
    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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); ?> + 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' => $buttonModifyStatus . $buttonCounter++, 'class' => $buttonModifyStatus)); + ?>