diff --git a/config/config-sample.ini b/config/config-sample.ini index 7e400c0..af7466a 100644 --- a/config/config-sample.ini +++ b/config/config-sample.ini @@ -76,6 +76,7 @@ password = password database = ska-db [ldap] +enabled = 0 ; Address to connect to LDAP server host = ldaps://ldap.example.com:636 ; Use StartTLS for connection security (recommended if using ldap:// instead diff --git a/core.php b/core.php index 618c15b..45acbb5 100644 --- a/core.php +++ b/core.php @@ -35,10 +35,12 @@ require('ldap.php'); require('email.php'); -$ldap_options = array(); -$ldap_options[LDAP_OPT_PROTOCOL_VERSION] = 3; -$ldap_options[LDAP_OPT_REFERRALS] = !empty($config['ldap']['follow_referrals']); -$ldap = new LDAP($config['ldap']['host'], $config['ldap']['starttls'], $config['ldap']['bind_dn'], $config['ldap']['bind_password'], $ldap_options); +if ($config['ldap']['enabled'] == 1) { + $ldap_options = array(); + $ldap_options[LDAP_OPT_PROTOCOL_VERSION] = 3; + $ldap_options[LDAP_OPT_REFERRALS] = !empty($config['ldap']['follow_referrals']); + $ldap = new LDAP($config['ldap']['host'], $config['ldap']['starttls'], $config['ldap']['bind_dn'], $config['ldap']['bind_password'], $ldap_options); +} setup_database(); $relative_frontend_base_url = (string)parse_url($config['web']['baseurl'], PHP_URL_PATH); diff --git a/migrations/004.php b/migrations/004.php new file mode 100644 index 0000000..9f088b7 --- /dev/null +++ b/migrations/004.php @@ -0,0 +1,159 @@ +store_result()) { + $res->free(); + } + } while ($database->more_results() && $database->next_result()); +} + +$this->database->autocommit(FALSE); + +$result = $this->database->query(" + SELECT uid FROM user WHERE uid = 'keys-sync' +"); +if ($result) { + if($result->num_rows === 0) { + $result->close(); + $result = $this->database->multi_query(" + INSERT INTO entity SET type = 'user'; + INSERT INTO user SET entity_id = ( + SELECT LAST_INSERT_ID() + ), uid = 'keys-sync', name = 'Synchronization script', email = '', auth_realm = 'local', admin = 1; + "); + free_results($this->database); + } else { + $result->close(); + $this->database->query(" + UPDATE user SET auth_realm = 'local', active = 1 WHERE uid = 'keys-sync'; + "); + } +} + + +$this->database->multi_query(" +CREATE TABLE `entity_event_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `entity_id` int(10) unsigned NOT NULL, + `actor_id` int(10) unsigned, + `date` datetime NOT NULL, + `details` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_entity_event_entity_id` (`entity_id`), + KEY `FK_entity_event_actor_id` (`actor_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT entity_event_2 SELECT * FROM entity_event; + +DROP TABLE entity_event; +RENAME TABLE entity_event_2 TO entity_event; + +ALTER TABLE `entity_event` + ADD CONSTRAINT `FK_entity_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_entity_event_entity_id` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE; +"); +free_results($this->database); + + +$this->database->multi_query(" +CREATE TABLE `group_event_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `group` int(10) unsigned NOT NULL, + `entity_id` int(10) unsigned, + `date` datetime NOT NULL, + `details` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_group_event_group` (`group`), + KEY `FK_group_event_entity` (`entity_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; + +INSERT group_event_2 SELECT * FROM group_event; + +DROP TABLE group_event; +RENAME TABLE group_event_2 TO group_event; + +ALTER TABLE `group_event` + ADD CONSTRAINT `FK_group_event_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_group_event_group` FOREIGN KEY (`group`) REFERENCES `group` (`entity_id`) ON DELETE CASCADE; +"); +free_results($this->database); + + +$this->database->multi_query(" +CREATE TABLE `group_member_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `group` int(10) unsigned NOT NULL, + `entity_id` int(10) unsigned NOT NULL, + `add_date` datetime NOT NULL, + `added_by` int(10) unsigned, + PRIMARY KEY (`id`), + UNIQUE KEY `group_entity_id` (`group`, `entity_id`), + KEY `FK_group_member_entity` (`entity_id`), + KEY `FK_group_member_entity_2` (`added_by`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT; + +INSERT group_member_2 SELECT * FROM group_member; + +DROP TABLE group_member; +RENAME TABLE group_member_2 TO group_member; + +ALTER TABLE `group_member` + ADD CONSTRAINT `FK_group_member_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `FK_group_member_entity_2` FOREIGN KEY (`added_by`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_group_member_group` FOREIGN KEY (`group`) REFERENCES `group` (`entity_id`) ON DELETE CASCADE +"); +free_results($this->database); + + +$this->database->multi_query(" +CREATE TABLE `server_event_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `actor_id` int(10) unsigned, + `date` datetime NOT NULL, + `details` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_server_log_server` (`server_id`), + KEY `FK_server_event_actor_id` (`actor_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT server_event_2 SELECT * FROM server_event; + +DROP TABLE server_event; +RENAME TABLE server_event_2 TO server_event; + +ALTER TABLE `server_event` + ADD CONSTRAINT `FK_server_event_actor_id` FOREIGN KEY (`actor_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_server_log_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE; +"); +free_results($this->database); + + +$this->database->multi_query(" +CREATE TABLE `server_note_2` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `server_id` int(10) unsigned NOT NULL, + `entity_id` int(10) unsigned, + `date` datetime NOT NULL, + `note` mediumtext NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_server_note_server` (`server_id`), + KEY `FK_server_note_user` (`entity_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT server_note_2 SELECT * FROM server_note; + +DROP TABLE server_note; +RENAME TABLE server_note_2 TO server_note; + +ALTER TABLE `server_note` + ADD CONSTRAINT `FK_server_note_entity` FOREIGN KEY (`entity_id`) REFERENCES `entity` (`id`) ON DELETE SET NULL, + ADD CONSTRAINT `FK_server_note_server` FOREIGN KEY (`server_id`) REFERENCES `server` (`id`) ON DELETE CASCADE +"); +free_results($this->database); + +$this->database->commit(); + +$this->database->autocommit(TRUE); diff --git a/model/migrationdirectory.php b/model/migrationdirectory.php index fabbc84..dd6a60c 100644 --- a/model/migrationdirectory.php +++ b/model/migrationdirectory.php @@ -22,7 +22,7 @@ class MigrationDirectory extends DBDirectory { /** * Increment this constant to activate a new migration from the migrations directory */ - const LAST_MIGRATION = 3; + const LAST_MIGRATION = 4; public function __construct() { parent::__construct(); diff --git a/model/user.php b/model/user.php index 6b7774e..deba94c 100644 --- a/model/user.php +++ b/model/user.php @@ -66,6 +66,23 @@ public function update() { } } + /** + * Delete the given user. + */ + public function delete() { + if(is_null($this->entity_id)) throw new BadMethodCallException('User must be in directory before it can be removed'); + $stmt = $this->database->prepare("DELETE FROM entity WHERE id = ?"); + $stmt->bind_param('d', $this->entity_id); + $stmt->execute(); + $stmt->close(); + $stmt = $this->database->prepare("DELETE FROM user WHERE entity_id = ?"); + $stmt->bind_param('d', $this->entity_id); + $stmt->execute(); + $stmt->close(); + + $this->sync_remote_access(); + } + /** * Magic getter method - if superior field requested, return User object of user's superior * @param string $field to retrieve diff --git a/model/userdirectory.php b/model/userdirectory.php index 8227b6b..5ff6b82 100644 --- a/model/userdirectory.php +++ b/model/userdirectory.php @@ -45,13 +45,22 @@ public function add_user(User $user) { $user_active = $user->active; $user_admin = $user->admin; $user_email = $user->email; - $stmt = $this->database->prepare("INSERT INTO entity SET type = 'user'"); - $stmt->execute(); - $user->entity_id = $stmt->insert_id; - $stmt = $this->database->prepare("INSERT INTO user SET entity_id = ?, uid = ?, name = ?, email = ?, active = ?, admin = ?"); - $stmt->bind_param('dsssdd', $user->entity_id, $user_id, $user_name, $user_email, $user_active, $user_admin); - $stmt->execute(); - $stmt->close(); + try { + $stmt = $this->database->prepare("INSERT INTO entity SET type = 'user'"); + $stmt->execute(); + $user->entity_id = $stmt->insert_id; + $stmt = $this->database->prepare("INSERT INTO user SET entity_id = ?, uid = ?, name = ?, email = ?, active = ?, admin = ?, auth_realm = ?"); + $stmt->bind_param('dsssdds', $user->entity_id, $user_id, $user_name, $user_email, $user_active, $user_admin, $user->auth_realm); + $stmt->execute(); + $stmt->close(); + } catch(mysqli_sql_exception $e) { + if($e->getCode() == 1062) { + // Duplicate entry + throw new UserAlreadyExistsException("User {$user->uid} already exists"); + } else { + throw $e; + } + } } /** @@ -82,6 +91,8 @@ public function get_user_by_id($id) { * @throws UserNotFoundException if no user with that uid exists */ public function get_user_by_uid($uid) { + global $config; + $ldap_enabled = $config['ldap']['enabled']; if(isset($this->cache_uid[$uid])) { return $this->cache_uid[$uid]; } @@ -93,11 +104,16 @@ public function get_user_by_uid($uid) { $user = new User($row['entity_id'], $row); $this->cache_uid[$uid] = $user; } else { - $user = new User; - $user->uid = $uid; - $this->cache_uid[$uid] = $user; - $user->get_details_from_ldap(); - $this->add_user($user); + if ($ldap_enabled == 1) { + $user = new User; + $user->uid = $uid; + $this->cache_uid[$uid] = $user; + $user->auth_realm = 'LDAP'; + $user->get_details_from_ldap(); + $this->add_user($user); + } else { + throw new UserNotFoundException('User does not exist.'); + } } $stmt->close(); return $user; @@ -148,3 +164,4 @@ public function list_users($include = array(), $filter = array()) { } class UserNotFoundException extends Exception {} +class UserAlreadyExistsException extends Exception {} diff --git a/requesthandler.php b/requesthandler.php index 5b921bf..cc60cd0 100644 --- a/requesthandler.php +++ b/requesthandler.php @@ -20,12 +20,6 @@ ob_start(); set_exception_handler('exception_handler'); -if(isset($_SERVER['PHP_AUTH_USER'])) { - $active_user = $user_dir->get_user_by_uid($_SERVER['PHP_AUTH_USER']); -} else { - throw new Exception("Not logged in."); -} - // Work out where we are on the server $base_path = dirname(__FILE__); $base_url = dirname($_SERVER['SCRIPT_NAME']); @@ -33,6 +27,17 @@ $relative_request_url = preg_replace('/^'.preg_quote($base_url, '/').'/', '/', $request_url); $absolute_request_url = 'http'.(isset($_SERVER['HTTPS']) ? 's' : '').'://'.$_SERVER['HTTP_HOST'].$request_url; +if(isset($_SERVER['PHP_AUTH_USER'])) { + try { + $active_user = $user_dir->get_user_by_uid($_SERVER['PHP_AUTH_USER']); + } catch(UserNotFoundException $ex) { + require('views/error403.php'); + die; + } +} else { + throw new Exception("Not logged in."); +} + if(empty($config['web']['enabled'])) { require('views/error503.php'); die; @@ -40,6 +45,7 @@ if(!$active_user->active) { require('views/error403.php'); + die; } if(!empty($_POST)) { @@ -62,10 +68,11 @@ if(isset($router->view)) { $view = path_join($base_path, 'views', $router->view.'.php'); if(file_exists($view)) { - if($active_user->auth_realm == 'LDAP' || $router->public) { + if($active_user->auth_realm == 'LDAP' || $active_user->auth_realm == 'local' || $router->public) { require($view); } else { require('views/error403.php'); + die; } } else { throw new Exception("View file $view missing."); diff --git a/scripts/ldap_update.php b/scripts/ldap_update.php index 6f8668f..076fca6 100755 --- a/scripts/ldap_update.php +++ b/scripts/ldap_update.php @@ -29,22 +29,28 @@ $active_user->uid = 'keys-sync'; $active_user->name = 'Synchronization script'; $active_user->email = ''; + $active_user->auth_realm = 'local'; $active_user->active = 1; $active_user->admin = 1; $active_user->developer = 0; $user_dir->add_user($active_user); } -try { - $sysgrp = $group_dir->get_group_by_name($config['ldap']['admin_group_cn']); -} catch(GroupNotFoundException $e) { - $sysgrp = new Group; - $sysgrp->name = $config['ldap']['admin_group_cn']; - $sysgrp->system = 1; - $group_dir->add_group($sysgrp); +$ldap_enabled = $config['ldap']['enabled']; + +if($ldap_enabled == 1) { + try { + $sysgrp = $group_dir->get_group_by_name($config['ldap']['admin_group_cn']); + } catch(GroupNotFoundException $e) { + $sysgrp = new Group; + $sysgrp->name = $config['ldap']['admin_group_cn']; + $sysgrp->system = 1; + $group_dir->add_group($sysgrp); + } } + foreach($users as $user) { - if($user->auth_realm == 'LDAP') { + if($user->auth_realm == 'LDAP' && $ldap_enabled == 1) { $active = $user->active; try { $user->get_details_from_ldap(); diff --git a/scripts/sync.php b/scripts/sync.php index 208818b..1390e32 100755 --- a/scripts/sync.php +++ b/scripts/sync.php @@ -69,6 +69,7 @@ $active_user->uid = 'keys-sync'; $active_user->name = 'Synchronization script'; $active_user->email = ''; + $active_user->auth_realm = 'local'; $active_user->active = 1; $active_user->admin = 1; $active_user->developer = 0; diff --git a/templates/functions.php b/templates/functions.php index aaf4184..758d395 100644 --- a/templates/functions.php +++ b/templates/functions.php @@ -85,7 +85,11 @@ function show_event($event) { group->name) ?> + actor->uid)) { ?> +
Username | -Full name | -Public keys | -
---|---|---|
uid)?> | -name)?> | -list_public_keys())))?> | -
get('users')); out(number_format($total).' user'.($total == 1 ? '' : 's').' found')?>
+Username | +Full name | +Public keys | +
---|---|---|
uid)?> | +name)?> | +list_public_keys())))?> | +