new: add basic experimental support for PostgreSQL

pull/1485/head
Andreas Ziegler 2016-08-18 06:30:05 +02:00
parent 9656913bd1
commit 9bf0e16ac6
7 changed files with 114 additions and 14 deletions

View File

@ -61,10 +61,12 @@ class DATABASE_CONFIG {
public $default = array(
'datasource' => 'Database/Mysql',
//'datasource' => 'Database/Postgres',
'persistent' => false,
'host' => 'localhost',
'login' => 'db login',
'port' => 3306,
'port' => 3306, // MySQL & MariaDB
//'port' => 5432, // PostgreSQL
'password' => 'db password',
'database' => 'misp',
'prefix' => '',

View File

@ -1,11 +1,13 @@
<?php
class UserInitShell extends AppShell {
public $uses = array('User', 'Role', 'Organisation', 'Server');
public $uses = array('User', 'Role', 'Organisation', 'Server', 'ConnectionManager');
public function main() {
if (!Configure::read('Security.salt')) {
$this->loadModel('Server');
$this->Server->serverSettingsSaveValue('Security.salt', $this->User->generateRandomPassword(32));
}
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
$dataSource = $dataSourceConfig['datasource'];
$this->Role->Behaviors->unload('SysLogLogable.SysLogLogable');
$this->User->Behaviors->unload('SysLogLogable.SysLogLogable');
// populate the DB with the first role (site admin) if it's empty
@ -29,6 +31,11 @@ class UserInitShell extends AppShell {
'perm_template' => 1
));
$this->Role->save($siteAdmin);
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
if ($dataSource == 'Database/Postgres') {
$sql = "SELECT setval('roles_id_seq', (SELECT MAX(id) FROM roles));";
$this->Role->query($sql);
}
}
if ($this->Organisation->find('count', array('conditions' => array('Organisation.local' => true))) == 0) {
@ -41,6 +48,11 @@ class UserInitShell extends AppShell {
'local' => 1
));
$this->Organisation->save($org);
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
if ($dataSource == 'Database/Postgres') {
$sql = "SELECT setval('organisations_id_seq', (SELECT MAX(id) FROM organisations));";
$this->Organisation->query($sql);
}
$org_id = $this->Organisation->id;
} else {
$hostOrg = $this->Organisation->find('first', array('conditions' => array('Organisation.name' => Configure::read('MISP.org')), 'recursive' => -1));
@ -69,6 +81,11 @@ class UserInitShell extends AppShell {
));
$this->User->validator()->remove('password'); // password is to simple, remove validation
$this->User->save($admin);
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
if ($dataSource == 'Database/Postgres') {
$sql = "SELECT setval('users_id_seq', (SELECT MAX(id) FROM users));";
$this->User->query($sql);
}
echo $authkey . PHP_EOL;
} else {
echo 'Script aborted: MISP instance already initialised.' . PHP_EOL;

View File

@ -22,6 +22,7 @@
// TODO GPG encryption has issues when keys are expired
App::uses('ConnectionManager', 'Model');
App::uses('Controller', 'Controller');
App::uses('File', 'Utility');
App::uses('RequestRearrangeTool', 'Tools');
@ -79,6 +80,13 @@ class AppController extends Controller {
);
public function beforeFilter() {
// check for a supported datasource configuration
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
$dataSource = $dataSourceConfig['datasource'];
if ($dataSource != 'Database/Mysql' && $dataSource != 'Database/Postgres') {
throw new Exception('datasource not supported: ' . $dataSource);
}
$this->set('jsVersion', $this->__jsVersion);
$this->loadModel('User');
$auth_user_fields = $this->User->describeAuthFields();

View File

@ -556,6 +556,8 @@ class UsersController extends AppController {
// $this->redirect($this->Auth->redirectUrl());
$this->redirect(array('controller' => 'events', 'action' => 'index'));
} else {
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
$dataSource = $dataSourceConfig['datasource'];
// don't display authError before first login attempt
if (str_replace("//","/",$this->webroot . $this->Session->read('Auth.redirect')) == $this->webroot && $this->Session->read('Message.auth.message') == $this->Auth->authError) {
$this->Session->delete('Message.auth');
@ -586,6 +588,11 @@ class UsersController extends AppController {
'perm_tagger' => 1,
));
$this->Role->save($siteAdmin);
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
if ($dataSource == 'Database/Postgres') {
$sql = "SELECT setval('roles_id_seq', (SELECT MAX(id) FROM roles));";
$this->Role->query($sql);
}
}
if ($this->User->Organisation->find('count', array('conditions' => array('Organisation.local' => true))) == 0) {
$org = array('Organisation' => array(
@ -599,6 +606,11 @@ class UsersController extends AppController {
'nationality' => ''
));
$this->User->Organisation->save($org);
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
if ($dataSource == 'Database/Postgres') {
$sql = "SELECT setval('organisations_id_seq', (SELECT MAX(id) FROM organisations));";
$this->User->Organisation->query($sql);
}
$org_id = $this->User->Organisation->id;
} else {
$hostOrg = $this->User->Organisation->find('first', array('conditions' => array('Organisation.name' => Configure::read('MISP.org'), 'Organisation.local' => true), 'recursive' => -1));
@ -625,6 +637,11 @@ class UsersController extends AppController {
));
$this->User->validator()->remove('password'); // password is too simple, remove validation
$this->User->save($admin);
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
if ($dataSource == 'Database/Postgres') {
$sql = "SELECT setval('users_id_seq', (SELECT MAX(id) FROM users));";
$this->User->query($sql);
}
}
}
}
@ -693,6 +710,7 @@ class UsersController extends AppController {
));
$orgs = array();
foreach ($temp as $t) {
if (!isset($t['Event'])) $t['Event'] = $t[0]; // Postgres workaround, array element has index 0 instead of Event
$orgs[$t['Event']['orgc_id']] = $t['Orgc']['name'];
}
// What org posted what type of attribute

View File

@ -1,15 +1,23 @@
<?php
App::uses('AppModel', 'Model');
App::uses('ConnectionManager', 'Model');
App::uses('Sanitize', 'Utility');
class Bruteforce extends AppModel {
public function insert($ip, $username) {
$expire = time() + Configure::read('SecureAuth.expire');
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
$dataSource = $dataSourceConfig['datasource'];
// sanitize fields
$ip = Sanitize::clean($ip);
$username = Sanitize::clean($username);
$this->query("INSERT INTO bruteforces (ip, username, `expire`) VALUES ('$ip', '$username', '$expire');");
if ($dataSource == 'Database/Mysql') {
$sql = "INSERT INTO bruteforces (ip, username, `expire`) VALUES ('$ip', '$username', '$expire');";
} else if ($dataSource == 'Database/Postgres') {
$sql = "INSERT INTO bruteforces (ip, username, expire) VALUES ('$ip', '$username', '$expire');";
}
$this->query($sql);
if ($this->isBlacklisted($ip, $username)) {
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
@ -25,7 +33,14 @@ class Bruteforce extends AppModel {
}
public function clean() {
$this->query("DELETE FROM bruteforces WHERE `expire` <= NOW();");
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
$dataSource = $dataSourceConfig['datasource'];
if ($dataSource == 'Database/Mysql') {
$sql = 'DELETE FROM bruteforces WHERE `expire` <= NOW();';
} else if ($dataSource == 'Database/Postgres') {
$sql = 'DELETE FROM bruteforces WHERE expire <= NOW();';
}
$this->query($sql);
}
public function isBlacklisted($ip,$username) {

View File

@ -82,6 +82,8 @@ class Log extends AppModel {
}
public function returnDates($org = 'all') {
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
$dataSource = $dataSourceConfig['datasource'];
$conditions = array();
$this->Organisation = ClassRegistry::init('Organisation');
if ($org !== 'all') {
@ -90,12 +92,27 @@ class Log extends AppModel {
$conditions['org'] = $org['Organisation']['name'];
}
$conditions['AND']['NOT'] = array('action' => array('login', 'logout', 'changepw'));
$validDates = $this->find('all', array(
'fields' => array('DISTINCT UNIX_TIMESTAMP(DATE(created)) AS Date', 'count(id) AS count'),
'conditions' => $conditions,
'group' => array('Date'),
'order' => array('Date')
));
if ($dataSource == 'Database/Mysql') {
$validDates = $this->find('all', array(
'fields' => array('DISTINCT UNIX_TIMESTAMP(DATE(created)) AS Date', 'count(id) AS count'),
'conditions' => $conditions,
'group' => array('Date'),
'order' => array('Date')
));
} else if ($dataSource == 'Database/Postgres') {
// manually generate the query for Postgres
// cakephp ORM would escape "DATE" datatype in CAST expression
$condnotinaction = "'" . implode("', '", $conditions['AND']['NOT']['action']) . "'";
if (!empty($conditions['org'])) $condOrg = ' AND org = "' . $conditions['org'] . '"';
else $condOrg = '';
$sql = 'SELECT DISTINCT EXTRACT(EPOCH FROM CAST(created AS DATE)) AS "Date",
COUNT(id) AS count
FROM logs
WHERE action NOT IN (' . $condnotinaction . ')
' . $condOrg . '
GROUP BY "Date" ORDER BY "Date"';
$validDates = $this->query($sql);
}
$data = array();
foreach ($validDates as $k => $date) {
$data[$date[0]['Date']] = intval($date[0]['count']);

View File

@ -1,5 +1,6 @@
<?php
App::uses('AppModel', 'Model');
App::uses('ConnectionManager', 'Model');
class Organisation extends AppModel{
public $useTable = 'organisations';
@ -192,13 +193,20 @@ class Organisation extends AppModel{
if (empty($currentOrg) || empty($targetOrg)) throw new MethodNotAllowedException('Something went wrong with the organisation merge. Organisation not found.');
$dir = new Folder();
$this->Log = ClassRegistry::init('Log');
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
$dataSource = $dataSourceConfig['datasource'];
$dirPath = APP . 'tmp' . DS . 'logs' . DS . 'merges';
if (!$dir->create($dirPath)) throw new MethodNotAllowedException('Merge halted because the log directory (default: /var/www/MISP/app/tmp/logs/merges) could not be created. This is most likely a permission issue, make sure that MISP can write to the logs directory and try again.');
$logFile = new File($dirPath . DS . 'merge_' . $currentOrg['Organisation']['id'] . '_' . $targetOrg['Organisation']['id'] . '_' . time() . '.log');
if (!$logFile->create()) throw new MethodNotAllowedException('Merge halted because the log file (default location: /var/www/MISP/app/tmp/logs/merges/[old_org_id]_[new_org_id]_timestamp.log) could not be created. This is most likely a permission issue, make sure that MISP can write to the logs directory and try again.');
$backupFile = new File($dirPath . DS . 'merge_' . $currentOrg['Organisation']['id'] . '_' . $targetOrg['Organisation']['id'] . '_' . time() . '.sql');
if (!$backupFile->create()) throw new MethodNotAllowedException('Merge halted because the backup script file (default location: /var/www/MISP/app/tmp/logs/merges/[old_org_id]_[new_org_id]_timestamp.sql) could not be created. This is most likely a permission issue, make sure that MISP can write to the logs directory and try again.');
$backupFile->append('INSERT INTO organisations (`' . implode('`, `', array_keys($currentOrg['Organisation'])) . '`) VALUES (\'' . implode('\', \'', array_values($currentOrg['Organisation'])) . '\');' . PHP_EOL);
if ($dataSource == 'Database/Mysql') {
$sql = 'INSERT INTO organisations (`' . implode('`, `', array_keys($currentOrg['Organisation'])) . '`) VALUES (\'' . implode('\', \'', array_values($currentOrg['Organisation'])) . '\');';
} else if ($dataSource == 'Database/Postgres') {
$sql = 'INSERT INTO organisations ("' . implode('", "', array_keys($currentOrg['Organisation'])) . '") VALUES (\'' . implode('\', \'', array_values($currentOrg['Organisation'])) . '\');';
}
$backupFile->append($sql . PHP_EOL);
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
@ -214,14 +222,29 @@ class Organisation extends AppModel{
$success = true;
foreach ($this->organisationAssociations as $model => $data) {
foreach ($data['fields'] as $field) {
$temp = $this->query('SELECT `id` FROM `' . $data['table'] . '` WHERE `' . $field . '` = "' . $currentOrg['Organisation']['id'] . '"');
if ($dataSource == 'Database/Mysql') {
$sql = 'SELECT `id` FROM `' . $data['table'] . '` WHERE `' . $field . '` = "' . $currentOrg['Organisation']['id'] . '"';
} else if ($dataSource == 'Database/Postgres') {
$sql = 'SELECT "id" FROM "' . $data['table'] . '" WHERE "' . $field . '" = "' . $currentOrg['Organisation']['id'] . '"';
}
$temp = $this->query($sql);
if (!empty($temp)) {
$dataMoved['values_changed'][$model][$field] = Set::extract('/' . $data['table'] . '/id', $temp);
if (!empty($dataMoved['values_changed'][$model][$field])) {
$this->Log->create();
try {
$result = $this->query('UPDATE `' . $data['table'] . '` SET `' . $field . '` = ' . $targetOrg['Organisation']['id'] . ' WHERE `' . $field . '` = ' . $currentOrg['Organisation']['id'] . ';');
$backupFile->append('UPDATE `' . $data['table'] . '` SET `' . $field . '` = ' . $currentOrg['Organisation']['id'] . ' WHERE `id` IN (' . implode(',', $dataMoved['values_changed'][$model][$field]) . ');' . PHP_EOL);
if ($dataSource == 'Database/Mysql') {
$sql = 'UPDATE `' . $data['table'] . '` SET `' . $field . '` = ' . $targetOrg['Organisation']['id'] . ' WHERE `' . $field . '` = ' . $currentOrg['Organisation']['id'] . ';';
} else if ($dataSource == 'Database/Postgres') {
$sql = 'UPDATE "' . $data['table'] . '" SET "' . $field . '" = ' . $targetOrg['Organisation']['id'] . ' WHERE "' . $field . '" = ' . $currentOrg['Organisation']['id'] . ';';
}
$result = $this->query($sql);
if ($dataSource == 'Database/Mysql') {
$sql = 'UPDATE `' . $data['table'] . '` SET `' . $field . '` = ' . $currentOrg['Organisation']['id'] . ' WHERE `id` IN (' . implode(',', $dataMoved['values_changed'][$model][$field]) . ');';
} else if ($dataSource == 'Database/Postgres') {
$sql = 'UPDATE "' . $data['table'] . '" SET "' . $field . '" = ' . $currentOrg['Organisation']['id'] . ' WHERE "id" IN (' . implode(',', $dataMoved['values_changed'][$model][$field]) . ');';
}
$backupFile->append($sql . PHP_EOL);
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Organisation',