Commit 3e8b8a82 authored by Côme Bernigaud's avatar Côme Bernigaud
Browse files

Fixes #2919 Migrated stepMigrate to simplePlugin

Still to do:
add vars to the beginning of the class
check that migrations works
create admin role in top of admin user
add migration features for outsideUsers, outsideGroups and organizationalUnits

Conflicts:
	setup/class_setupStep_Migrate.inc
	setup/setup_migrate.tpl
parent 98df7059
......@@ -43,12 +43,6 @@ class Step_Checks extends setupStep
);
}
function __construct()
{
parent::__construct();
$this->update_strings();
}
function update_strings()
{
$this->s_title = _("Installation check");
......
......@@ -30,7 +30,7 @@ check_gosaAccounts - Check if there are users without the required obje
migrate_gosaAccounts - Migrate selected users to FusionDirectory user accounts.
check_organizationalUnits - Check if there are departments, that are not visible for FusionDirectory
migrate_organizationalUnits - Migrate selected departments
check_administrativeAccount - Check if there is at least one acl entry available
check_adminAccount - Check if there is at least one acl entry available
checkBase - Check if there is a root object available
get_user_list - Get list of available users
......@@ -44,79 +44,169 @@ array_to_ldif - Create ldif output of an ldap result array
****************/
class Step_Migrate extends setup_step
class CheckFailedException extends Exception
{
var $header_image = "geticon.php?context=applications&icon=utilities-system-monitor&size=48";
var $languages = array();
var $attributes = array('valid_admin');
var $checks = array();
private $error;
/* Department migration attributes */
var $dep_migration_dialog = FALSE;
var $deps_to_migrate = array();
var $show_details = FALSE;
public function __construct($msg, $error)
{
parent::__construct($msg);
$this->error = $error;
}
/* Department migration attributes */
var $users_migration_dialog = FALSE;
var $users_to_migrate = array();
public function getError()
{
return $this->error;
}
}
/* Create Acl attributes */
var $acl_create_dialog = FALSE;
var $acl_create_selected = ""; // Currently selected element, that should receive admin rights
var $acl_create_changes = ""; // Contains ldif information about changes
var $acl_create_confirmed = FALSE;
class StepMigrateDialog extends GenericDialog
{
protected $post_cancel = 'dialog_cancel';
protected $post_finish = 'dialog_confirm';
/* Checks initialised ? */
var $checks_initialised = FALSE;
private $infos;
private $tplfile;
private $check;
/* Users outside to people ou */
var $outside_users = array();
var $outside_users_dialog = FALSE;
public function __construct(&$check, $tpl, $infos)
{
$this->attribute = NULL;
$this->dialog = NULL;
$this->infos = $infos;
$this->tplfile = $tpl;
$this->check = $check;
}
/* Users outside to groups ou */
var $outside_groups = array();
var $outside_groups_dialog = FALSE;
public function dialog_execute()
{
if (
isset($_POST['dialog_showchanges']) ||
isset($_POST['dialog_hidechanges']) ||
isset($_POST['dialog_refresh'])) {
$this->infos = $this->check->dialog_refresh();
}
$smarty = get_smarty();
$smarty->assign('infos', $this->infos);
return $smarty->fetch(get_template_path($this->tplfile, TRUE, dirname(__FILE__)));
}
/* Device migration */
var $device_dialog = FALSE;
var $device = array();
function handle_finish ()
{
if ($this->check->migrate_confirm()) {
return FALSE;
} else {
return $this->dialog_execute();
}
}
/* Service migration */
var $service_dialog = FALSE;
var $service = array();
function handle_cancel ()
{
return FALSE;
}
}
/* Group menus */
var $menu_dialog = FALSE;
var $menu = array();
class StepMigrateCheck
{
public $name;
public $title;
public $status = FALSE;
public $msg = '';
public $error = '';
public $fnc;
private $step;
public function __construct($step, $name, $title)
{
$this->name = $name;
$this->title = $title;
$this->fnc = 'check_'.$name;
$this->step = $step;
}
/* check for multiple use of same uidNumber */
var $check_uidNumbers = array();
var $check_uidNumbers_dialog = FALSE;
public function run($fnc = NULL)
{
if ($fnc === NULL) {
$fnc = $this->fnc;
}
try {
$this->msg = _('Ok');
$this->error = $this->step->$fnc($this);
$this->status = TRUE;
} catch (CheckFailedException $e) {
$this->status = FALSE;
$this->msg = $e->getMessage();
$this->error = $e->getError();
}
}
/* check for multiple use of same gidNumber */
var $check_gidNumbers = array();
var $check_gidNumbers_dialog = FALSE;
public function save_object()
{
if (isset($_POST[$this->name.'_create'])) {
$fnc = $this->fnc.'_create';
$this->step->$fnc($this);
} elseif (isset($_POST[$this->name.'_migrate'])) {
$fnc = $this->fnc.'_migrate';
$this->step->$fnc($this);
}
}
var $group_list = array();
public function submit ($value = NULL, $id = 'migrate')
{
if ($value === NULL) {
$value = _('Migrate');
}
return '<input type="submit" name="'.$this->name.'_'.$id.'" value="'.$value.'"/>';
}
/* Migrable users */
var $migrate_users = array();
var $acl_migrate_dialog = FALSE;
var $migrate_acl_base_entry = "";
public function migrate_confirm()
{
$fnc = $this->fnc.'_migrate'.'_confirm';
$res = $this->step->$fnc($this);
if ($res) {
$this->run();
}
return $res;
/* TODO rerun depending tests? (done by hand for now) */
}
public function dialog_refresh()
{
$fnc = $this->fnc.'_migrate'.'_refresh';
return $this->step->$fnc($this);
}
}
class Step_Migrate extends setupStep
{
var $header_image = "geticon.php?context=applications&icon=utilities-system-monitor&size=48";
/* Root object classes */
var $rootOC_migrate_dialog = FALSE;
var $rootOC_details = array();
/* One valid admin dn */
var $valid_admin = FALSE;
/* Invisible users */
var $users_to_migrate = array();
/* Defaults ACL roles */
var $defaultRoles;
static function getAttributesInfo()
{
return array(
'checks' => array(
'class' => array('fullwidth'),
'name' => _('PHP module and extension checks'),
'template' => get_template_path("setup_migrate.tpl", TRUE, dirname(__FILE__)),
'attrs' => array(
new FakeAttribute('checks')
)
),
);
}
function __construct()
{
$this->update_strings();
parent::__construct();
$this->fill_defaultRoles();
}
......@@ -153,339 +243,266 @@ class Step_Migrate extends setup_step
function initialize_checks()
{
$this->checks = array();
$this->checks['root']['TITLE'] = _("Checking for root object");
$this->checks['root']['STATUS'] = FALSE;
$this->checks['root']['STATUS_MSG'] = "";
$this->checks['root']['ERROR_MSG'] = "";
$this->checkBase();
$this->checks['rootOC']['TITLE'] = _("Inspecting object classes in root object");
$this->checks['rootOC']['STATUS'] = FALSE;
$this->checks['rootOC']['STATUS_MSG'] = "";
$this->checks['rootOC']['ERROR_MSG'] = "";
$this->checkBaseOC();
$this->checks['permissions']['TITLE'] = _("Checking permission for LDAP database");
$this->checks['permissions']['STATUS'] = FALSE;
$this->checks['permissions']['STATUS_MSG'] = "";
$this->checks['permissions']['ERROR_MSG'] = "";
$this->check_ldap_permissions();
$this->checks['deps_visible']['TITLE'] = _("Checking for invisible departments");
$this->checks['deps_visible']['STATUS'] = FALSE;
$this->checks['deps_visible']['STATUS_MSG'] = "";
$this->checks['deps_visible']['ERROR_MSG'] = "";
$this->checks['users_visible']['TITLE'] = _("Checking for invisible users");
$this->checks['users_visible']['STATUS'] = FALSE;
$this->checks['users_visible']['STATUS_MSG'] = "";
$this->checks['users_visible']['ERROR_MSG'] = "";
$this->check_gosaAccounts();
$this->migrate_users = array();
$this->checks['acls']['TITLE'] = _("Checking for super administrator");
$this->checks['acls']['STATUS'] = FALSE;
$this->checks['acls']['STATUS_MSG'] = "";
$this->checks['acls']['ERROR_MSG'] = "";
$this->check_administrativeAccount();
$this->checks['default_acls']['TITLE'] = _("Checking for default ACL roles and groupes");
$this->checks['default_acls']['STATUS'] = FALSE;
$this->checks['default_acls']['STATUS_MSG'] = "";
$this->checks['default_acls']['ERROR_MSG'] = "";
$this->check_defaultACLs();
$this->checks['outside_users']['TITLE'] = _("Checking for users outside the people tree");
$this->checks['outside_users']['STATUS'] = FALSE;
$this->checks['outside_users']['STATUS_MSG'] = "";
$this->checks['outside_users']['ERROR_MSG'] = "";
$this->search_outside_users();
$this->checks['outside_groups']['TITLE'] = _("Checking for groups outside the groups tree");
$this->checks['outside_groups']['STATUS'] = FALSE;
$this->checks['outside_groups']['STATUS_MSG'] = "";
$this->checks['outside_groups']['ERROR_MSG'] = "";
$this->search_outside_groups();
$this->check_organizationalUnits();
$this->checks['uidNumber_usage']['TITLE'] = _("Checking for duplicated user ids");
$this->checks['uidNumber_usage']['STATUS'] = FALSE;
$this->checks['uidNumber_usage']['STATUS_MSG'] = "";
$this->checks['uidNumber_usage']['ERROR_MSG'] = "";
$this->check_uidNumber();
$this->checks['gidNumber_usage']['TITLE'] = _("Checking for duplicate group ids");
$this->checks['gidNumber_usage']['STATUS'] = FALSE;
$this->checks['gidNumber_usage']['STATUS_MSG'] = "";
$this->checks['gidNumber_usage']['ERROR_MSG'] = "";
$this->check_gidNumber();
$this->checks['old_style_devices']['TITLE'] = _("Checking for old style USB devices");
$this->checks['old_style_devices']['STATUS'] = FALSE;
$this->checks['old_style_devices']['STATUS_MSG'] = "";
$this->checks['old_style_devices']['ERROR_MSG'] = "";
$this->check_usb_devices();
$this->checks['old_style_services']['TITLE'] = _("Checking for old services that have to be migrated");
$this->checks['old_style_services']['STATUS'] = FALSE;
$this->checks['old_style_services']['STATUS_MSG'] = "";
$this->checks['old_style_services']['ERROR_MSG'] = "";
$this->check_services();
$this->checks['old_style_menus']['TITLE'] = _("Checking for old style application menus");
$this->checks['old_style_menus']['STATUS'] = FALSE;
$this->checks['old_style_menus']['STATUS_MSG'] = "";
$this->checks['old_style_menus']['ERROR_MSG'] = "";
$this->check_menus();
$checks = array(
'base' => new StepMigrateCheck($this, 'base', _('Checking for root object')),
'baseOC' => new StepMigrateCheck($this, 'baseOC', _('Inspecting object classes in root object')),
'permissions' => new StepMigrateCheck($this, 'permissions', _('Checking permission for LDAP database')),
'gosaAccounts' => new StepMigrateCheck($this, 'gosaAccounts', _('Checking for invisible users')),
'adminAccount' => new StepMigrateCheck($this, 'adminAccount', _('Checking for super administrator')),
'defaultACLs' => new StepMigrateCheck($this, 'defaultACLs', _('Checking for default ACL roles and groups')),
'outsideUsers' => new StepMigrateCheck($this, 'outsideUsers', _('Checking for users outside the people tree')),
'outsideGroups' => new StepMigrateCheck($this, 'outsideGroups', _('Checking for groups outside the groups tree')),
'organizationalUnits' => new StepMigrateCheck($this, 'organizationalUnits', _('Checking for invisible departments')),
'uidNumber' => new StepMigrateCheck($this, 'uidNumber', _('Checking for duplicated UID numbers')),
'gidNumber' => new StepMigrateCheck($this, 'gidNumber', _('Checking for duplicate GID numbers')),
);
$this->checks = $checks;
}
function execute()
{
if (empty($this->checks) || isset($_POST['reload'])) {
$this->initialize_checks();
foreach ($this->checks as &$check) {
$check->run();
}
unset($check);
}
return parent::execute();
}
/* Check if there are uidNumbers which are used more than once */
function check_uidNumber()
function save_object()
{
parent::save_object();
foreach ($this->checks as &$check) {
$check->save_object();
}
unset($check);
}
/* Check if the root object exists.
* If the parameter just_check is TRUE, then just check if the
* root object is missing and update the info messages.
* If the Parameter is FALSE, try to create a new root object.
*/
function check_base(&$checkobj)
{
global $config;
$ldap = $config->get_ldap_link();
/* Check if root object exists */
$ldap->cd($config->current['BASE']);
$res = $ldap->search("(&(objectClass=posixAccount)(uidNumber=*))", array("dn","uidNumber"));
if (!$res) {
$this->checks['uidNumber_usage']['STATUS'] = FALSE;
$this->checks['uidNumber_usage']['STATUS_MSG'] = _("LDAP query failed");
$this->checks['uidNumber_usage']['ERROR_MSG'] = _("Possibly the 'root object' is missing.");
return FALSE;
}
$ldap->set_size_limit(1);
$res = $ldap->search("(objectClass=*)");
$ldap->set_size_limit(0);
$err = ldap_errno($ldap->cid);
$this->check_uidNumbers = array();
$tmp = array();
while ($attrs = $ldap->fetch()) {
$tmp[$attrs['uidNumber'][0]][] = $attrs;
}
if ( !$res ||
$err == 0x20 || // LDAP_NO_SUCH_OBJECT
$err == 0x40) { // LDAP_NAMING_VIOLATION
foreach ($tmp as $entries) {
if (count($entries) > 1) {
foreach ($entries as $entry) {
$this->check_uidNumbers[base64_encode($entry['dn'])] = $entry;
}
}
/* Root object doesn't exists */
throw new CheckFailedException(
_('Failed'),
_('The LDAP root object is missing. It is required to use your LDAP service.').'&nbsp;'.
$checkobj->submit(_('Try to create root object'), 'create')
);
}
if ($this->check_uidNumbers) {
$this->checks['uidNumber_usage']['STATUS'] = FALSE;
$this->checks['uidNumber_usage']['STATUS_MSG'] = "<div style='color:#F0A500'>"._("Warning")."</div>";
$this->checks['uidNumber_usage']['ERROR_MSG'] =
sprintf(_("Found %s duplicate values for attribute 'uidNumber'."), count($this->check_uidNumbers));
return FALSE;
} else {
$this->checks['uidNumber_usage']['STATUS'] = TRUE;
$this->checks['uidNumber_usage']['STATUS_MSG'] = _("Ok");
$this->checks['uidNumber_usage']['ERROR_MSG'] = "";
return TRUE;
}
/* Root object exists */
return '';
}
function check_base_create (&$check)
{
global $config;
$ldap = $config->get_ldap_link();
/* Check if there are duplicated gidNumbers present in ldap */
function check_gidNumber()
/* Add root object */
$ldap->cd($config->current['BASE']);
$ldap->create_missing_trees($config->current['BASE']);
/* Re-run test */
$checkobj->run();
}
/* Check if the root object includes the required object classes, e.g. gosaDepartment is required for ACLs.
* If the parameter just_check is TRUE, then just check for the OCs.
* If the Parameter is FALSE, try to add the required object classes.
*/
function check_baseOC(&$checkobj)
{
global $config;
$ldap = $config->get_ldap_link();
/* Check if root object exists */
$ldap->cd($config->current['BASE']);
$res = $ldap->search("(&(objectClass=posixGroup)(gidNumber=*))", array("dn","gidNumber"));
if (!$res) {
$this->checks['gidNumber_usage']['STATUS'] = FALSE;
$this->checks['gidNumber_usage']['STATUS_MSG'] = _("LDAP query failed");
$this->checks['gidNumber_usage']['ERROR_MSG'] = _("Possibly the 'root object' is missing.");
return FALSE;
$ldap->cat($config->current['BASE']);
if (!$ldap->count()) {
throw new CheckFailedException(
_('LDAP query failed'),
_('Possibly the "root object" is missing.')
);
}
$this->check_gidNumbers = array();
$tmp = array();
while ($attrs = $ldap->fetch()) {
$tmp[$attrs['gidNumber'][0]][] = $attrs;
}
$attrs = $ldap->fetch();
foreach ($tmp as $entries) {
if (count($entries) > 1) {
foreach ($entries as $entry) {
$this->check_gidNumbers[base64_encode($entry['dn'])] = $entry;
/* Root object doesn't exists */
if (!in_array("gosaDepartment", $attrs['objectClass'])) {
$this->rootOC_details = array();
$mods = array();
/* Get list of possible container objects, to be able to detect naming
* attributes and missing attribute types.
*/
if (!class_available("departmentManagement")) {
throw new CheckFailedException(
_("Failed"),
sprintf(_("Missing FusionDirectory object class '%s'!"), "departmentManagement").
"&nbsp;"._("Please check your installation.")
);
}
/* Try to detect base class type, e.g. is it a dcObject */
$dep_types = departmentManagement::getDepartmentTypes();
$dep_type = "";
$attrs['objectClass'][] = 'gosaDepartment'; // This allow us to filter it as if it was already migrated
foreach ($dep_types as $type) {
if (objects::isOfType($attrs, $type)) {
$dep_type = $type;
break;
}
}
}
$key = array_search('gosaDepartment', $attrs['objectClass']);
unset($attrs['objectClass'][$key]);
if ($this->check_gidNumbers) {
$this->checks['gidNumber_usage']['STATUS'] = FALSE;
$this->checks['gidNumber_usage']['STATUS_MSG'] = "<div style='color:#F0A500'>"._("Warning")."</div>";
$this->checks['gidNumber_usage']['ERROR_MSG'] =
sprintf(_("Found %s duplicate values for attribute 'gidNumber'."), count($this->check_gidNumbers));
return FALSE;
} else {
$this->checks['gidNumber_usage']['STATUS'] = TRUE;
$this->checks['gidNumber_usage']['STATUS_MSG'] = _("Ok");
$this->checks['gidNumber_usage']['ERROR_MSG'] = "";
return TRUE;
}
}
/* If no known base class was detect, abort with message */
if (empty($dep_type)) {
throw new CheckFailedException(
_("Failed"),
sprintf(_("Cannot handle the structural object type of your root object. Please try to add the object class '%s' manually."), "gosaDepartment")
);
}
$dep_infos = objects::infos($dep_type);
/* Search for groups outside the group ou */
function search_outside_groups()
{
global $config;
$ldap = $config->get_ldap_link();
/* Create 'current' and 'target' object properties, to be able to display
* a set of modifications required to create a valid FusionDirectory department.
*/
$str = "dn: ".$config->current['BASE']."\n";
for ($i = 0; $i < $attrs['objectClass']['count']; $i++) {
$str .= "objectClass: ".$attrs['objectClass'][$i]."\n";
}
$this->rootOC_details['current'] = $str;
$group_ou = get_ou('groupRDN');
$ldap->cd($config->current['BASE']);
/* Create target infos */
$str = "dn: ".$config->current['BASE']."\n";
for ($i = 0; $i < $attrs['objectClass']['count']; $i++) {
$str .= "objectClass: ".$attrs['objectClass'][$i]."\n";
$mods['objectClass'][] = $attrs['objectClass'][$i];
}
$mods['objectClass'][] = "gosaDepartment";
/***********
* Get all gosaDepartments to be able to
* validate correct ldap tree position of every single user
***********/
$valid_deps = array();
$valid_deps['/'] = $config->current['BASE'];
$ldap->search("(&(objectClass=gosaDepartment)(ou=*))", array("dn","ou"));
while ($attrs = $ldap->fetch()) {
$valid_deps[] = $attrs['dn'];
}
$str .= "<b>objectClass: gosaDepartment</b>\n";
/***********
* Get all groups
***********/
$res = $ldap->search("(objectClass=posixGroup)", array("dn"));
if (!$res) {
$this->checks['outside_groups']['STATUS'] = FALSE;
$this->checks['outside_groups']['STATUS_MSG'] = _("LDAP query failed");
$this->checks['outside_groups']['ERROR_MSG'] = _("Possibly the 'root object' is missing.");
return FALSE;
}
/* Append attribute 'ou', it is required by gosaDepartment */
if (!isset($attrs['ou'])) {
$val = "GOsa";
if (isset($attrs[$dep_infos['mainAttr']][0])) {
$val = $attrs[$dep_infos['mainAttr']][0];
}
$str .= "<b>ou: ".$val."</b>\n";
$this->outside_groups = array();
$this->groups_list = array();;
while ($attrs = $ldap->fetch()) {
$group_db_base = preg_replace("/^[^,]+,".preg_quote($group_ou, '/')."/i", "", $attrs['dn']);
$mods['ou'] = $val;
}