Commit 89eb19cc authored by Adrian Rossouw's avatar Adrian Rossouw Committed by adrian

refactored database_mysql, added helper functions for finding out group name...

refactored database_mysql, added helper functions for finding out group name and file owner name. added functionality to stop functions from being run via the web. slightly updated documentation
parent 63b79693
......@@ -384,13 +384,16 @@ function provision_token_list($type = 'all') {
/**
* Confirm that provision is running through Drush.
*
* This module requires a 'bootstrap' and 'configtest' feature, which is to be used during installation, to make sure that
* all the provisioning modules have the correct permissions / access to be able to do their jobs. This bootstrap process should
* be used when deploying new platforms (ie: new releases).
* A safety mechanism to ensure that the drush commands are not run through the web front end.
*/
function _provision_confirm_drush() {
function provision_confirm_drush() {
#confirm that code is running through the command line.
if (php_isapi_name() != 'cli') {
drupal_set_message(t("This function should only be called via the command line Drush utility."));
return false;
}
return true;
}
/**
......@@ -413,4 +416,50 @@ function provision_shell_exec() {
$command = call_user_func_array("sprintf", $args);
return drush_shell_exec($command);
}
/**
* Set the active database.
*
* Wrapper around db_set_active, which provides switching out of db_url.
* @param new_db_url
* The database url to set the connection to. If not provided, will switch back to Drupal default.
*/
function provision_set_active_db($new_db_url = null) {
static $old_db_url = null;
global $db_url;
#initialize static
if (!$old_db_url) {
$old_db_url = $db_url;
}
if ($new_db_url) {
$db_url = $new_db_url;
db_set_active();
}
else {
$db_url = $old_db_url;
db_set_active();
}
}
/**
* Return the user who owns this script.
*
* Used to generate permissions. Can be overridden by variable_set().
*/
function provision_get_script_owner() {
return variable_get('provision_user', get_current_user());
}
/**
* Return the group who runs the httpd daemin.
*
* Used to generate permissions. Can be overridden by variable_set().
*/
function provision_get_group_name() {
$info = posix_getgrgid(posix_getgid());
return variable_get('provision_group', $info['name']);
}
\ No newline at end of file
......@@ -93,13 +93,9 @@ function provision_help($section) {
return $output;
case 'admin/help/provision#requirements' :
return t("some text here");
}
}
/**
* Implementation of hook_perm().
*/
......@@ -223,15 +219,17 @@ function provision_drush_command() {
'description' => t('Generate a back up for the site.')
);
/*
$items['provision enable'] = array(
'callback' => '_provision_enable',
'arguments' => array('domain.com' => t('The domain of the site to enable (only if enabled).')),
'description' => 'Enable a disabled site.'
);
$items['provision disable'] = array(
'callback' => '_provision_disable',
'arguments' => array('domain.com' => t('The domain of the site to disable (only if disabled).')),
'description' => 'Disable a site.'
);
/*
// Not implemented yet.
$items['provision delete'] = array(
'callback' => '_provision_delete',
......@@ -270,13 +268,15 @@ function provision_drush_command() {
* Will exit with a PROVISION_FRAMEWORK_ERROR if the command is incorrectly used.
*/
function provision_install_cmd($url) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
global $args;
$data = provision_get_site_data($url);
if (!$args['commands'][2]) {
print "Usage: drush.php provision install DOMAIN [OPTIONS]\n";
print "Install a new site for the domain DOMAIN.\n";
print "Example: drush.php provision install mydomain.com --site-db-host=localhost\n";
print "Example: drush.php provision install mydomain.com --site-db-host localhost\n";
provision_set_log("error", "Incorrect usage of the provisioning framework");
drupal_set_error(PROVISION_FRAMEWORK_ERROR);
provision_output($url, $data);
......@@ -312,7 +312,9 @@ function provision_install_cmd($url) {
* @return
* Boolean denoting whether the provision_invoke rolled back changes made.
*/
function _provision_pre_install($url, &$data) {
function _provision_pre_install($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$rolled_back = provision_invoke("pre_install", $url, $data);
if (!provision_get_error()) {
......@@ -342,6 +344,8 @@ function _provision_pre_install($url, &$data) {
* Boolean denoting whether the provision_invoke rolled back changes made.
*/
function _provision_install($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return;
$rolled_back = provision_invoke("install", $url, $data);
# if (!$rolled_back) {
......@@ -368,6 +372,8 @@ function _provision_install($url, &$data) {
* Boolean denoting whether the provision_invoke rolled back changes made.
*/
function _provision_post_install($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$rolled_back = provision_invoke("post_install", $url, $data);
if (!$rolled_back) {
$data['site-installed'] = true;
......@@ -385,6 +391,8 @@ function _provision_post_install($url, &$data) {
* Will exit with a PROVISION_SITE_NOT_FOUND error if the site does not exist.
*/
function _provision_synch($url) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
if (!_provision_drupal_site_exists($url)) {
provision_log("Error", "Site has not been installed yet.");
drupal_set_error(PROVISION_SITE_NOT_FOUND);
......@@ -410,6 +418,8 @@ function _provision_synch($url) {
* Will exit with a PROVISION_SITE_NOT_FOUND error if the site does not exist.
*/
function _provision_backup($url) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
if (!_provision_drupal_site_exists($url)) {
provision_log("Error", "Site has not been installed yet.");
provision_set_error(PROVISION_SITE_NOT_FOUND);
......@@ -460,6 +470,9 @@ function _provision_backup($url) {
* Will exit with a PROVISION_SITE_NOT_FOUND error if the site does not exist.
*/
function _provision_import($url) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
if (!_provision_drupal_site_exists($url)) {
provision_log("Error", "Site directory is not present, and can not be imported.");
drupal_set_error(PROVISION_SITE_NOT_FOUND);
......
......@@ -21,9 +21,20 @@ function provision_apache_help($section) {
switch ($section) {
case 'admin/help/provision#requirements':
$output .= "<ol>";
$output .= '<li>' . t('<strong>The user account running the script, and the group of the httpd daemon.</strong> The provision framework takes special care to make sure that the file permissions of the hosted sites are always as safe as can be, especially to make sure that the web server does not have the ability to modify the code of the site, therefor this information is required to assure that safety while keeping the sites accessible.') . '</li>';
$output .= '<li>' . t('<strong>Access to the server\'s <code>httpd.conf</code> file.</strong> You are required to add a single line to the httpd.conf file, which allows the system to load the additional virtual hosts that are generated.') . '</li>';
$output .= '<li>' . t('<strong>Ability to reload the httpd daemon.</strong> As the provisioning framework should <strong>never</strong> be run as root, and the web server group should <strong>never</strong> be allowed access to the functionality to stop/start the web server, it is required that you provide access to the Apache restart command for the user account the script will be running as. If this is not configured, every command will ask for a sudo password when restarting the server.') . '</li>';
$output .= '<li>' . t('<strong>The user account running the script, and the group of the httpd daemon.</strong>
The provision framework takes special care to make sure that the file permissions of the
hosted sites are always as safe as can be, especially to make sure that the web server does
not have the ability to modify the code of the site, therefor this information is required
to assure that safety while keeping the sites accessible.') . '</li>';
$output .= '<li>' . t('<strong>Access to the server\'s <code>httpd.conf</code> file.</strong>
You are required to add a single line to the httpd.conf file, which allows
the system to load the additional virtual hosts that are generated.') . '</li>';
$output .= '<li>' . t('<strong>Ability to reload the httpd daemon.</strong>
As the provisioning framework should <strong>never</strong> be run as root,
and the web server group should <strong>never</strong> be allowed access to the
functionality to stop/start the web server, it is required that you provide access
to the Apache restart command for the user account the script will be running as.
If this is not configured, every command will ask for a sudo password when restarting the server.') . '</li>';
$output .= "</ol>";
return $output;
break;
......@@ -81,28 +92,45 @@ function _provision_apache_default_template() {
EOF;
}
function provision_apache_provision_pre_install($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
return _provision_apache_create_vhost_config($url, $data);
}
function provision_apache_provision_post_install($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
return _provision_apache_restart_apache();
}
function provision_apache_provision_enable($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
_provision_apache_create_vhost_config($url, $data);
_provision_apache_restart_apache();
}
function provision_apache_provision_disable($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
_provision_apache_delete_vhost_config($url, $data);
_provision_apache_restart_apache();
}
function provision_apache_provision_regenerate($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
_provision_apache_create_vhost_config($url, $data);
_provision_apache_restart_apache();
}
function _provision_apache_delete_vhost_config($url, $data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$vhost_path = variable_get('provision_apache_vhost_path', 'vhost.d');
if (file_exists()) {
unlink($vhost_path . '/' . $url);
......@@ -110,6 +138,9 @@ function _provision_apache_delete_vhost_config($url, $data) {
}
function _provision_apache_create_vhost_config($url, $data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$vhost_path = variable_get('provision_apache_vhost_path', 'vhost.d');
$file = fopen($vhost_path . '/' . $url, "w");
if (!$file) {
......@@ -124,6 +155,9 @@ function _provision_apache_create_vhost_config($url, $data) {
}
function _provision_apache_restart_apache() {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
# This is required to be configurable, due to the fact that different hosts might need to do this differently.
# TODO : add configuration / test for this
$apache_restart_cmd = escapeshellcmd(variable_get('provision_apache_restart_cmd', 'sudo apachectl graceful'));
......@@ -132,4 +166,4 @@ function _provision_apache_restart_apache() {
provision_set_error(PROVISION_WEB_ERROR);
provision_log("error", "Web server could not be restarted. Changes might not be available until this has been done.");
}
}
\ No newline at end of file
}
......@@ -71,6 +71,9 @@ function provision_drupal_provision_configure() {
* If the file exists, return TRUE, else return FALSE.
*/
function _provision_drupal_site_exists($url) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
return file_exists("sites/$url/settings.php");
}
......@@ -79,6 +82,9 @@ function _provision_drupal_site_exists($url) {
* Implentation of hook_provision_backup()
*/
function provision_drupal_provision_backup($url, $data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
// Adds the site directory into the backup file
provision_log("backup", "Adding sites directory to $data[backup_file].gz");
$result = provision_shell_exec("cd %s; tar -rf %s * ", "sites/$url", $data['backup_file']);
......@@ -119,6 +125,9 @@ END;
* because the modules might provide additional information about the site.
*/
function _provision_drupal_create_settings_file($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$fp = fopen("sites/$url/settings.php", "w");
$text = variable_get('provision_settings_template', _provision_drupal_default_template());
fwrite($fp, token_replace($text, 'site', $data));
......@@ -138,6 +147,9 @@ function _provision_drupal_create_settings_file($url, &$data) {
* Also maintains permissions on existing directories.
*/
function _provision_drupal_create_directories($url, $profile = null) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$paths = array(
"sites/$url" => '0750',
"sites/$url/files" => '2750',
......@@ -171,6 +183,9 @@ function _provision_drupal_create_directories($url, $profile = null) {
* main provisioning site.
*/
function _provision_drupal_switch_active_site($url = null) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
static $backups;
if ($url) {
/* Pretend to be the site being installed */
......@@ -242,6 +257,9 @@ function _provision_drupal_switch_active_site($url = null) {
* Force drupal to load the modules it expects to find on an uninstalled site
*/
function _provision_drupal_force_load_modules($url = null) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
static $backup_list;
if ($url) {
$backup_list = module_list();
......@@ -266,6 +284,9 @@ function _provision_drupal_force_load_modules($url = null) {
* Install the drupal schema and install profile
*/
function _provision_drupal_install_schema($profile) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
// Load the profile.
require_once "./profiles/$profile/$profile.profile";
......@@ -301,3 +322,11 @@ function _provision_drupal_install_schema($profile) {
}
}
/**
* implementation of provision_verify
*/
function provision_drupal_provision_verify() {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
}
\ No newline at end of file
......@@ -19,7 +19,21 @@
switch ($section) {
case 'admin/help/provision#requirements':
$output .= "<ol>";
$output .= '<li>' . t('<strong>Mysql user account capable of creating new databases.</strong> To be able to create new sites, the provisioning framework will need to be able to create new databases and users. It is not recommended using the mysql root password for this, but any account with the correct permissions will do.') . '</li>';
$command = <<<EOF
benton:~ adrian$ mysql -uroot -pXXXXXXXXX mysql
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1329 to server version: 4.1.20
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> grant create, drop, grant option on *.* to 'username_here'@'localhost'
-> identified by 'mypassword';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
EOF;
$output .= '<li>' . t('<strong>Mysql user account capable of creating new databases.</strong> To be able to create new sites, the provisioning framework will need to be able to create new databases and users. It is not recommended using the mysql root password for this, but any account with the correct permissions will do. To create the account, log in to your server as root, and type in the following command: <pre>@command_text</pre>', array('@command_text' => $command)) . '</li>';
$output .= "</ol>";
return $output;
break;
......@@ -71,52 +85,131 @@ function provision_mysql_provision_configure() {
function provision_mysql_provision_pre_install($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$data['site-db-type'] = 'mysql'; # only support innodb. for now.
$data['site-db-host'] = ($data['site-db-host']) ? $data['site-db-host'] : variable_get('provision_mysql_host', 'localhost');
$data['site-db-passwd'] = user_password(); # generate a random password for use
if ($data['site_id']) {
$data['site-db-name'] = 'site_' . $data['site_id'];
if ($data['site-id']) {
$data['site-db-name'] = 'site_' . $data['site-id'];
$data['site-db-username'] = $data['site-db-name']; // mysql has some really really stupid rules about who db / usernames, so site id is the safest.
}
else {
$data['site-db-name'] = substr(ereg_replace("^www\.", "", str_replace('-', '_', str_replace(".", "", $url))), 0, 10);
$data['site-db-username'] = $data['site-db-name'];
// TODO : A reasonable fallback if the site id isn't available. This is going to make it a bit harder to test at first, but that's ok.
$data['site-db-name'] = ereg_replace("^www\.", "", str_replace('-', '_', str_replace(".", "", $url)));
$data['site-db-username'] = substr($data['site-db-name'], 0, 16);
}
# For this to work, the user account the provisioning site has been set up with, requires CREATE database permissions.
# TODO : Add additional configuration for a database account to use for these , but this is the quickest way to get the code up and running.
$db_url = sprintf("mysqli://%s:%s@%s/mysql", variable_get('provision_mysql_user', 'root'), variable_get('provision_mysql_password', 'root'), $data['site-db-host'] );
if ( db_result(db_query("SHOW DATABASES LIKE '%s'", $data['site-db-name'])) ) {
db_query("DROP DATABASE %s", $data['site-db-name']);
provision_set_active_db(_provision_master_db_url());
if ( _provision_mysql_database_exists($data['site-db-name']) ) {
_provision_drop_database($data['site-db-name']);
}
db_query("CREATE DATABASE %s", $data['site-db-name']);
_provision_create_database($data['site-db-name']);
if ( !db_result(db_query("SHOW DATABASES LIKE '%s'", $data['site-db-name'])) ) {
if ( !_provision_mysql_database_exists($data['site-db-name']) ) {
provision_set_error(PROVISION_DB_ERROR);
provision_log("error", "Database could not be created.");
provision_set_active_db();
return FALSE;
}
db_query("GRANT ALL PRIVILEGES ON %s.* TO %s@`%%` IDENTIFIED BY '%s'", $data['site-db-name'], $data['site-db-username'], $data['site-db-passwd']);
db_query("GRANT ALL PRIVILEGES ON %s.* TO %s@%s IDENTIFIED BY '%s'",$data['site-db-name'], $data['site-db-username'], $data['site-db-host'], $data['site-db-passwd']);
_provision_mysql_grant($data['site-db-name'], $data['site-db-username'], $data['site-db-passwd']);
_provision_mysql_grant($data['site-db-name'], $data['site-db-username'], $data['site-db-passwd'], $data['site-db-host']);
if ($data['site-mysql-old-passwords']) {
db_query("SET PASSWORD FOR '%s'@'%%' = OLD_PASSWORD('%s')", $data['site-db-username'], $data['site-db-passwd']);
db_query("SET PASSWORD FOR %s@%s = OLD_PASSWORD('%s')", $data['site-db-username'], $data['site-db-host'], $data['site-db-passwd']);
_provision_mysql_old_password($data['site-db-username'], $data['site-db-passwd']);
_provision_mysql_old_password($data['site-db-username'], $data['site-db-passwd'], $data['site-db-host']);
}
db_query("FLUSH PRIVILEGES");
_provision_mysql_flush();
provision_set_active_db();
#TODO : Test to confirm that the database is actually writeable. Taking this on faith for now.
}
function provision_mysql_provision_backup($url, &$data) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
provision_log("backup", "Generating mysql dump for $url.");
provision_shell_exec("mysqldump -u%s -p%s %s > sites/%s/database.sql", $data['site-db-username'], $data['site-db-passwd'], $data['site-db-name'], $url);
provision_shell_exec("cd sites/%; tar -rf %s database.sql; rm database.sql", $url, $data['backup_file']);
}
function _provision_mysql_database_exists($name) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
return db_result(db_query("SHOW DATABASES LIKE '%s'", $data['site-db-name']));
}
function _provision_mysql_drop_database($name) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
db_query("DROP DATABASE %s", $name);
}
function _provision_mysql_create_database($name) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
db_query("CREATE DATABASE %s", $name);
}
function _provision_mysql_can_create_database() {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$test = 'provision_test';
_provision_mysql_create_database($test);
if (_provision_mysql_database_exists($test)) {
_provision_mysql_drop_database($test);
return true;
}
return false;
}
function _provision_mysql_grant($name, $username, $password, $host = '') {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$host = ($host) ? $host : '%';
db_query("GRANT ALL PRIVILEGES ON %s.* TO %s@`%s` IDENTIFIED BY '%s'", $name, $username, $host, $password);
}
function _provision_mysql_old_password($username, $password, $host = '') {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$host = ($host) ? $host : '%';
db_query("SET PASSWORD FOR '%s'@'%s' = OLD_PASSWORD('%s')", $username, $host, $password);
}
function _provision_mysql_flush() {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
db_query("FLUSH PRIVILEGES");
}
function _provision_master_db_url() {
return sprintf("mysql://%s:%s@%s/mysql", variable_get('provision_mysql_user', 'root'), variable_get('provision_mysql_password', 'root'), $data['site-db-host']);
}
/**
* Implementation of hook_provision_verify
*/
function provision_mysql_provision_verify() {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
provision_set_active_db(_provision_master_db_url());
if (!_provision_mysql_can_create_database()) {
provision_set_error(PROVISION_DB_ERROR | PROVISION_FRAMEWORK_ERROR);
provision_log('error', t('Unable to create new databases.'));
}
provision_set_active_db();
}
......@@ -9,6 +9,9 @@ function provision_stats_drush_command() {
}
function _provision_stats($url) {
#safety mechanism to ensure back end calls are not made via the front end.
if (!provision_confirm_drush()) return null;
$data = provision_get_site_data($url);
if (!$data['site-installed']) {
print t('The site %site has not been installed yet.', array('%site' => $url));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment