An error occurred while loading the file. Please try again.
-
Côme Chilliet authored
Also improved --migrate-users
ca39c88d
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2007 Fabian Hickert
Copyright (C) 2011-2015 FusionDirectory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/****************
* FUNCTIONS
Step_Migrate - Constructor.
update_strings - Used to update the displayed step informations.
initialize_checks - Initialize migration steps.
check_ldap_permissions - Check if the used admin account has full access to the ldap database.
check_gosaAccounts - Check if there are users without the required objectClasses.
migrate_gosaAccounts - Migrate selected users to FusionDirectory user accounts.
check_orgUnits - Check if there are departments, that are not visible for FusionDirectory
migrate_orgUnits - Migrate selected departments
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
create_admin
create_admin_user
execute - Generate html output of this plugin
save_object - Save posts
array_to_ldif - Create ldif output of an ldap result array
****************/
class CheckFailedException extends Exception
{
private $error;
public function __construct($msg, $error)
{
parent::__construct($msg);
$this->error = $error;
}
public function getError()
{
return $this->error;
}
}
class StepMigrateDialog extends GenericDialog
{
protected $post_cancel = 'dialog_cancel';
protected $post_finish = 'dialog_confirm';
private $infos;
private $tplfile;
private $check;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
public function __construct(&$check, $tpl, $infos)
{
$this->attribute = NULL;
$this->dialog = NULL;
$this->infos = $infos;
$this->tplfile = $tpl;
$this->check = $check;
}
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__)));
}
function handle_finish ()
{
if ($this->check->migrate_confirm()) {
return FALSE;
} else {
return $this->dialog_execute();
}
}
function handle_cancel ()
{
return FALSE;
}
}
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;
}
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();
}
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
}
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);
}
}
public function submit ($value = NULL, $id = 'migrate')
{
if ($value === NULL) {
$value = _('Migrate');
}
return '<input type="submit" name="'.$this->name.'_'.$id.'" value="'.$value.'"/>';
}
public function migrate_confirm()
{
$fnc = $this->fnc.'_migrate'.'_confirm';
$res = $this->step->$fnc($this);
if ($res) {
$this->run();
}
return $res;
}
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_details = array();
/* Entries needing migration */
var $orgUnits_toMigrate = array();
var $gosaAccounts_toMigrate = array();
var $outsideUsers_toMigrate = array();
var $outsideGroups_toMigrate = array();
/* check for multiple use of same uidNumber */
var $check_uidNumbers = array();
/* check for multiple use of same gidNumber */
var $check_gidNumbers = 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')
)
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
),
);
}
function __construct()
{
parent::__construct();
$this->fill_defaultRoles();
}
function update_strings()
{
$this->s_title = _("LDAP inspection");
$this->s_title_long = _("LDAP inspection");
$this->s_info = _("Analyze your current LDAP for FusionDirectory compatibility");
}
function fill_defaultRoles()
{
$this->defaultRoles = array(
array(
'cn' => 'manager',
'description' => _('Give all rights on users in the given branch'),
'objectclass' => array('top', 'gosaRole'),
'gosaAclTemplate' => '0:user/password;cmdrw,user/user;cmdrw,user/posixAccount;cmdrw'
),
array(
'cn' => 'editowninfos',
'description' => _('Allow users to edit their own information (main tab and posix use only on base)'),
'objectclass' => array('top', 'gosaRole'),
'gosaAclTemplate' => '0:user/posixAccount;srw,user/user;srw'
),
array(
'cn' => 'editowninfos',
'description' => _('Allow users to edit their own password (use only on base)'),
'objectclass' => array('top', 'gosaRole'),
'gosaAclTemplate' => '0:user/password;srw'
),
);
}
function initialize_checks()
{
global $config;
$config->get_departments();
$checks = array(
'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')),
'orgUnits' => new StepMigrateCheck($this, 'orgUnits', _('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;
}
/* Return ldif information for a given attribute array */
function array_to_ldif($attrs)
{
$ret = '';
unset($attrs['count']);
unset($attrs['dn']);
foreach ($attrs as $name => $value) {
if (is_numeric($name)) {
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
continue;
}
if (is_array($value)) {
unset($value['count']);
foreach ($value as $a_val) {
$ret .= $name.': '. $a_val."\n";
}
} else {
$ret .= $name.': '. $value."\n";
}
}
return preg_replace("/\n$/", '', $ret);
}
function execute()
{
if (empty($this->checks) || isset($_POST['reload'])) {
$this->initialize_checks();
foreach ($this->checks as $check) {
$check->run();
}
}
return parent::execute();
}
function save_object()
{
$this->is_completed = TRUE;
parent::save_object();
foreach ($this->checks as $check) {
$check->save_object();
}
}
/* 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']);
$ldap->cat($config->current['BASE']);
if (!$ldap->count()) {
throw new CheckFailedException(
_('LDAP query failed'),
_('Possibly the "root object" is missing.')
);
}
$attrs = $ldap->fetch();
/* 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").
" "._("Please check your installation.")
);
}
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
/* 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 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);
/* 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;
/* 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";
$str .= "<b>objectClass: gosaDepartment</b>\n";
/* 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";
$mods['ou'] = $val;
}
/*Append description, it is required by gosaDepartment too */
if (!isset($attrs['description'])) {
$val = "GOsa";
if (isset($attrs[$dep_infos['mainAttr']][0])) {
$val = $attrs[$dep_infos['mainAttr']][0];
}
$str .= "<b>description: ".$val."</b>\n";
$mods['description'] = $val;
}
$this->rootOC_details['target'] = $str;
$this->rootOC_details['mods'] = $mods;
/* Add button that allows to open the migration details */
throw new CheckFailedException(
_('Failed'),
' '.$checkobj->submit()
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
);
}
/* Create & remove of dummy object was successful */
return '';
}
function check_baseOC_migrate (&$checkobj)
{
$this->openDialog(new StepMigrateDialog($checkobj, 'setup_migrate_baseOC.tpl', $this->rootOC_details));
}
function check_baseOC_migrate_confirm ()
{
global $config;
$ldap = $config->get_ldap_link();
/* Check if root object exists */
$ldap->cd($config->current['BASE']);
$ldap->cat($config->current['BASE']);
$attrs = $ldap->fetch();
/* Root object doesn't exists */
if (!in_array("gosaDepartment", $attrs['objectClass'])) {
/* Add root object */
$ldap->cd($config->current['BASE']);
if (isset($this->rootOC_details['mods'])) {
$res = $ldap->modify($this->rootOC_details['mods']);
if (!$res) {
msg_dialog::display(_('LDAP error'), msgPool::ldaperror($ldap->get_error(), $config->current['BASE'], LDAP_MOD, get_class()), LDAP_ERROR);
}
$this->checks['adminAccount']->run();
return $res;
} else {
trigger_error('No modifications to make... ');
}
return TRUE;
}
return TRUE;
}
/* Check ldap accessibility
* Create and remove a dummy object,
* to ensure that we have the necessary permissions
*/
function check_permissions(&$checkobj)
{
global $config;
$ldap = $config->get_ldap_link();
/* Create dummy entry */
$name = 'GOsa_setup_text_entry_'.session_id().rand(0, 999999);
$dn = 'ou='.$name.','.$config->current['BASE'];
$testEntry = array();
$testEntry['objectClass'][] = 'top';
$testEntry['objectClass'][] = 'organizationalUnit';
$testEntry['objectClass'][] = 'gosaDepartment';
$testEntry['description'] = 'Created by FusionDirectory setup, this object can be removed.';
$testEntry['ou'] = $name;
/* check if simple ldap cat will be successful */
$res = $ldap->cat($config->current['BASE']);
if (!$res) {
throw new CheckFailedException(
_('LDAP query failed'),
_('Possibly the "root object" is missing.')
);
}
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
/* Try to create dummy object */
$ldap->cd ($dn);
$res = $ldap->add($testEntry);
$ldap->cat($dn);
if (!$ldap->count()) {
new log("view", "setup/".get_class($this), $dn, array(), $ldap->get_error());
throw new CheckFailedException(
_('Failed'),
sprintf(_('The specified user "%s" does not have full access to your LDAP database.'), $config->current['ADMINDN'])
);
}
/* Try to remove created entry */
$res = $ldap->rmDir($dn);
$ldap->cat($dn);
if ($ldap->count()) {
new log("view", "setup/".get_class($this), $dn, array(), $ldap->get_error());
throw new CheckFailedException(
_('Failed'),
sprintf(_('The specified user "%s" does not have full access to your ldap database.'), $config->current['ADMINDN'])
);
}
/* Create & remove of dummy object was successful */
return '';
}
/* Check if there are users which will
* be invisible for FusionDirectory
*/
function check_gosaAccounts(&$checkobj)
{
global $config;
$ldap = $config->get_ldap_link();
/* Remember old list of invisible users, to be able to set
* the 'html checked' status for the checkboxes again
*/
$old = $this->gosaAccounts_toMigrate;
$this->gosaAccounts_toMigrate = array();
/* Get all invisible users */
$ldap->cd($config->current['BASE']);
$res = $ldap->search(
'(&'.
'(|'.
'(objectClass=posixAccount)'.
'(objectClass=person)'.
'(objectClass=OpenLDAPperson)'.
')'.
'(!(objectClass=inetOrgPerson))'.
'(uid=*)'.
')',
array('sn','givenName','cn','uid')
);
while ($attrs = $ldap->fetch()) {
if (!preg_match('/,dc=addressbook,/', $attrs['dn'])) {
$attrs['checked'] = FALSE;
$attrs['before'] = "";
$attrs['after'] = "";
/* Set objects to selected, that were selected before reload */
if (isset($old[base64_encode($attrs['dn'])])) {
$attrs['checked'] = $old[base64_encode($attrs['dn'])]['checked'];
}
$this->gosaAccounts_toMigrate[base64_encode($attrs['dn'])] = $attrs;
}
}
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
if (!$res) {
throw new CheckFailedException(
_('LDAP query failed'),
_('Possibly the "root object" is missing.')
);
} elseif (count($this->gosaAccounts_toMigrate) == 0) {
/* No invisible */
return '';
} else {
throw new CheckFailedException(
"<div style='color:#F0A500'>"._("Warning")."</div>",
sprintf(
_('Found %s user(s) that will not be visible in FusionDirectory or which are incomplete.'),
count($this->gosaAccounts_toMigrate)
).$checkobj->submit()
);
}
}
function check_gosaAccounts_migrate (&$checkobj)
{
$this->check_multipleGeneric_migrate($checkobj, array('title' => _('User migration')));
}
function check_gosaAccounts_migrate_refresh (&$checkobj)
{
return $this->check_multipleGeneric_migrate_refresh($checkobj, array('title' => _('User migration')));
}
function check_gosaAccounts_migrate_confirm(&$checkobj, $only_ldif = FALSE)
{
return $this->check_multipleGeneric_migrate_confirm(
$checkobj,
array('inetOrgPerson','organizationalPerson','person'),
array(),
$only_ldif
);
}
function check_multipleGeneric_migrate (&$checkobj, $infos)
{
$var = $checkobj->name.'_toMigrate';
/* Fix displayed dn syntax */
$infos['entries'] = $this->$var;
foreach ($infos['entries'] as $key => $data) {
$infos['entries'][$key]['dn'] = LDAP::fix($data['dn']);
}
$this->openDialog(new StepMigrateDialog($checkobj, 'setup_migrate_gosaAccounts.tpl', $infos));
}
function check_multipleGeneric_migrate_refresh (&$checkobj, $infos)
{
if (isset($_POST['dialog_showchanges'])) {
/* Show changes */
$fnc = 'check_'.$checkobj->name.'_migrate_confirm';
$this->$fnc($checkobj, TRUE);
} else {
/* Hide changes */
$checkobj->run();
}
/* Fix displayed dn syntax */
$var = $checkobj->name.'_toMigrate';
$infos['entries'] = $this->$var;
foreach ($infos['entries'] as $key => $data) {
$infos['entries'][$key]['dn'] = LDAP::fix($data['dn']);
}
return $infos;
}
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
function check_multipleGeneric_migrate_confirm(&$checkobj, $oc, $mandatory, $only_ldif)
{
global $config;
$ldap = $config->get_ldap_link();
/* Add objectClasses to the selected entries */
$var = $checkobj->name.'_toMigrate';
foreach ($this->$var as $key => &$entry) {
$entry['checked'] = isset($_POST['migrate_'.$key]);
if ($entry['checked']) {
/* Get old objectClasses */
$ldap->cat($entry['dn'], array_merge(array('objectClass'), array_keys($mandatory)));
$attrs = $ldap->fetch();
/* Create new objectClass array */
$new_attrs = array();
$new_attrs['objectClass'] = $oc;
for ($i = 0; $i < $attrs['objectClass']['count']; $i++) {
if (!in_array_ics($attrs['objectClass'][$i], $new_attrs['objectClass'])) {
$new_attrs['objectClass'][] = $attrs['objectClass'][$i];
}
}
/* Append mandatories if missing */
foreach ($mandatory as $name => $value) {
if (!isset($attrs[$name])) {
$new_attrs[$name] = $value;
}
}
/* Set info attributes for current object,
* or write changes to the ldap database
*/
if ($only_ldif) {
$entry['before'] = $this->array_to_ldif($attrs);
$entry['after'] = $this->array_to_ldif($new_attrs);
} else {
$ldap->cd($attrs['dn']);
if (!$ldap->modify($new_attrs)) {
msg_dialog::display(
_('Migration error'),
sprintf(
_('Cannot migrate entry "%s":').'<br/><br/><i>%s</i>',
LDAP::fix($attrs['dn']), $ldap->get_error()
),
ERROR_DIALOG
);
return FALSE;
}
}
}
}
unset($entry);
return TRUE;
}
/* Check Acls if there is at least one object with acls defined */
function check_adminAccount(&$checkobj)
{
global $config;
/* Reset settings */
$FD_1_0_8_found = FALSE;
/* Establish ldap connection */
$ldap = $config->get_ldap_link();
$ldap->cd($config->current['BASE']);
$res = $ldap->cat($config->current['BASE']);
if (!$res) {