class_setupStep_Migrate.inc 44.4 KB
Newer Older
1
2
3
4
<?php
/*
  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
  Copyright (C) 2007  Fabian Hickert
5
  Copyright (C) 2011-2015  FusionDirectory
6
7
8
9
10
11
12
13
14
15
16
17
18

  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
19
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20
21
22
*/

/****************
23
 * FUNCTIONS
24
25
26
27
28
29

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.
30
migrate_gosaAccounts        - Migrate selected users to FusionDirectory user accounts.
31
32
check_orgUnits   - Check if there are departments, that are not visible for FusionDirectory
migrate_orgUnits - Migrate selected departments
33
check_adminAccount - Check if there is at least one acl entry available
34
checkBase                   - Check if there is a root object available
35
36

get_user_list               - Get list of available users
37
38
39

create_admin
create_admin_user
40
41

execute                     - Generate html output of this plugin
42
43
44
save_object                 - Save posts
array_to_ldif               - Create ldif output of an ldap result array

45
46
 ****************/

47
class CheckFailedException extends Exception
48
{
49
  private $error;
50

51
52
53
54
55
  public function __construct($msg, $error)
  {
    parent::__construct($msg);
    $this->error = $error;
  }
56

57
58
59
60
61
  public function getError()
  {
    return $this->error;
  }
}
62

63
64
65
66
class StepMigrateDialog extends GenericDialog
{
  protected $post_cancel = 'dialog_cancel';
  protected $post_finish = 'dialog_confirm';
67

68
69
70
  private $infos;
  private $tplfile;
  private $check;
71

72
73
74
75
76
77
78
79
  public function __construct(&$check, $tpl, $infos)
  {
    $this->attribute  = NULL;
    $this->dialog     = NULL;
    $this->infos      = $infos;
    $this->tplfile    = $tpl;
    $this->check      = $check;
  }
80

81
82
83
84
85
86
87
88
89
90
91
92
  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__)));
  }
93

94
95
96
97
98
99
100
101
  function handle_finish ()
  {
    if ($this->check->migrate_confirm()) {
      return FALSE;
    } else {
      return $this->dialog_execute();
    }
  }
102

103
104
105
106
107
  function handle_cancel ()
  {
    return FALSE;
  }
}
108

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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;
  }
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
  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();
    }
  }
142

143
144
145
146
147
148
149
150
151
152
  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);
    }
  }
153

154
155
156
157
158
159
160
  public function submit ($value = NULL, $id = 'migrate')
  {
    if ($value === NULL) {
      $value = _('Migrate');
    }
    return '<input type="submit" name="'.$this->name.'_'.$id.'" value="'.$value.'"/>';
  }
161

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  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";
182
183
184

  /* Root object classes */
  var $rootOC_details = array();
185

186
187
188
189
190
191
192
193
  /* 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();
194

195
196
  /* check for multiple use of same gidNumber */
  var $check_gidNumbers = array();
197

198
199
200
  /* Defaults ACL roles */
  var $defaultRoles;

201
202
203
204
205
206
207
208
209
210
211
212
213
214
  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')
        )
      ),
    );
  }

215
  function __construct()
216
  {
217
    parent::__construct();
218
    $this->fill_defaultRoles();
219
220
221
222
  }

  function update_strings()
  {
223
224
225
    $this->s_short_name   = _('LDAP inspection');
    $this->s_title        = _('LDAP inspection');
    $this->s_description  = _('Analyze your current LDAP for FusionDirectory compatibility');
226
227
  }

228
229
230
231
232
233
234
235
236
237
238
  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',
239
        'description'     => _('Allow users to edit their own information (main tab and posix use only on base)'),
240
241
242
243
244
245
246
247
248
249
250
251
        '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'
      ),
    );
  }

252
253
  function initialize_checks()
  {
254
255
256
    global $config;
    $config->get_departments();

257
    $checks = array(
258
259
260
261
262
263
264
265
266
267
      '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')),
268
269
270
271
272
    );

    $this->checks = $checks;
  }

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  /* 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)) {
        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);
  }

295
296
297
298
  function execute()
  {
    if (empty($this->checks) || isset($_POST['reload'])) {
      $this->initialize_checks();
299
      foreach ($this->checks as $check) {
300
301
302
303
        $check->run();
      }
    }
    return parent::execute();
304
305
  }

306
307
  function save_object()
  {
308
    $this->is_completed = TRUE;
309
    parent::save_object();
310
    foreach ($this->checks as $check) {
311
312
313
      $check->save_object();
    }
  }
314

315
316
317
318
319
  /* 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)
320
  {
321
322
    global $config;
    $ldap = $config->get_ldap_link();
323

324
    /* Check if root object exists */
325
    $ldap->cd($config->current['BASE']);
326
327
328
329
330
331
    $ldap->cat($config->current['BASE']);
    if (!$ldap->count()) {
      throw new CheckFailedException(
        _('LDAP query failed'),
        _('Possibly the "root object" is missing.')
      );
332
333
    }

334
    $attrs = $ldap->fetch();
335

336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
    /* 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;
360
361
        }
      }
362
363
      $key = array_search('gosaDepartment', $attrs['objectClass']);
      unset($attrs['objectClass'][$key]);
364

365
366
367
368
369
370
371
372
      /* 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);
373

374
375
376
377
378
379
380
381
      /* 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;
382

383
384
385
386
387
388
389
      /* 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";
390

391
      $str .= "<b>objectClass: gosaDepartment</b>\n";
392

393
394
395
396
397
398
399
      /* 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";
400

401
402
        $mods['ou'] = $val;
      }
403

404
405
406
407
408
409
410
411
412
      /*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;
413
      }
414
415
      $this->rootOC_details['target'] = $str;
      $this->rootOC_details['mods']   = $mods;
416

417
418
419
420
421
      /*  Add button that allows to open the migration details */
      throw new CheckFailedException(
        _('Failed'),
        '&nbsp;'.$checkobj->submit()
      );
422
    }
423
424
425

    /* Create & remove of dummy object was successful */
    return '';
426
427
  }

428
  function check_baseOC_migrate (&$checkobj)
429
  {
430
    $this->openDialog(new StepMigrateDialog($checkobj, 'setup_migrate_baseOC.tpl', $this->rootOC_details));
431
432
433
  }

  function check_baseOC_migrate_confirm ()
434
  {
435
436
437
    global $config;
    $ldap = $config->get_ldap_link();

438
    /* Check if root object exists */
439
    $ldap->cd($config->current['BASE']);
440
    $ldap->cat($config->current['BASE']);
441

442
    $attrs = $ldap->fetch();
443

444
445
446
447
448
449
450
451
452
453
454
455
456
    /* 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... ');
457
      }
458
      return TRUE;
459
    }
460
    return TRUE;
461
462
  }

463
464
  /* Check ldap accessibility
   * Create and remove a dummy object,
465
466
   *  to ensure that we have the necessary permissions
   */
467
  function check_permissions(&$checkobj)
468
  {
469
470
    global $config;
    $ldap = $config->get_ldap_link();
471

472
    /* Create dummy entry */
473
474
    $name       = 'GOsa_setup_text_entry_'.session_id().rand(0, 999999);
    $dn         = 'ou='.$name.','.$config->current['BASE'];
475
476
    $testEntry  = array();

477
478
479
480
    $testEntry['objectClass'][] = 'top';
    $testEntry['objectClass'][] = 'organizationalUnit';
    $testEntry['objectClass'][] = 'gosaDepartment';
    $testEntry['description']   = 'Created by FusionDirectory setup, this object can be removed.';
481
    $testEntry['ou']            = $name;
482

483
    /* check if simple ldap cat will be successful */
484
    $res = $ldap->cat($config->current['BASE']);
485
    if (!$res) {
486
487
488
489
      throw new CheckFailedException(
        _('LDAP query failed'),
        _('Possibly the "root object" is missing.')
      );
490
    }
491

492
    /* Try to create dummy object */
493
494
495
    $ldap->cd ($dn);
    $res = $ldap->add($testEntry);
    $ldap->cat($dn);
496
497
    if (!$ldap->count()) {
      new log("view", "setup/".get_class($this), $dn, array(), $ldap->get_error());
498
499
500
501
      throw new CheckFailedException(
        _('Failed'),
        sprintf(_('The specified user "%s" does not have full access to your LDAP database.'), $config->current['ADMINDN'])
      );
502
503
    }

504
    /* Try to remove created entry */
505
506
    $res = $ldap->rmDir($dn);
    $ldap->cat($dn);
507
508
    if ($ldap->count()) {
      new log("view", "setup/".get_class($this), $dn, array(), $ldap->get_error());
509
510
511
512
      throw new CheckFailedException(
        _('Failed'),
        sprintf(_('The specified user "%s" does not have full access to your ldap database.'), $config->current['ADMINDN'])
      );
513
514
515
    }

    /* Create & remove of dummy object was successful */
516
    return '';
517
  }
518

519
520
  /* Check if there are users which will
   *  be invisible for FusionDirectory
521
   */
522
  function check_gosaAccounts(&$checkobj)
523
  {
524
525
526
    global $config;
    $ldap = $config->get_ldap_link();

527
528
    /* Remember old list of invisible users, to be able to set
     *  the 'html checked' status for the checkboxes again
529
     */
530
531
    $old    = $this->gosaAccounts_toMigrate;
    $this->gosaAccounts_toMigrate = array();
532

533
    /* Get all invisible users */
534
    $ldap->cd($config->current['BASE']);
535
536
537
538
539
540
541
542
543
544
545
546
    $res = $ldap->search(
      '(&'.
        '(|'.
          '(objectClass=posixAccount)'.
          '(objectClass=organizationalPerson)'.
          '(objectClass=OpenLDAPperson)'.
        ')'.
        '(!(objectClass=inetOrgPerson))'.
        '(uid=*)'.
      ')',
      array('sn','givenName','cn','uid')
    );
547

548
    while ($attrs = $ldap->fetch()) {
549
      if (!preg_match('/,dc=addressbook,/', $attrs['dn'])) {
550
551
552
553
554
        $attrs['checked'] = FALSE;
        $attrs['before']  = "";
        $attrs['after']   = "";

        /* Set objects to selected, that were selected before reload */
555
        if (isset($old[base64_encode($attrs['dn'])])) {
556
557
          $attrs['checked'] = $old[base64_encode($attrs['dn'])]['checked'];
        }
558
        $this->gosaAccounts_toMigrate[base64_encode($attrs['dn'])] = $attrs;
559
560
561
      }
    }

562
    if (!$res) {
563
564
565
566
      throw new CheckFailedException(
        _('LDAP query failed'),
        _('Possibly the "root object" is missing.')
      );
567
    } elseif (count($this->gosaAccounts_toMigrate) == 0) {
568
569
      /* No invisible */
      return '';
570
    } else {
571
572
573
574
      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.'),
575
          count($this->gosaAccounts_toMigrate)
576
577
        ).$checkobj->submit()
      );
578
579
580
    }
  }

581
  function check_gosaAccounts_migrate (&$checkobj)
582
  {
583
    $this->check_multipleGeneric_migrate($checkobj, array('title' => _('User migration')));
584
585
  }

586
  function check_gosaAccounts_migrate_refresh (&$checkobj)
587
  {
588
    return $this->check_multipleGeneric_migrate_refresh($checkobj, array('title' => _('User migration')));
589
590
  }

591
  function check_gosaAccounts_migrate_confirm(&$checkobj, $only_ldif = FALSE)
592
593
  {
    return $this->check_multipleGeneric_migrate_confirm(
594
      $checkobj,
595
      array('inetOrgPerson','organizationalPerson','person'),
596
597
598
599
600
      array(),
      $only_ldif
    );
  }

601
  function check_multipleGeneric_migrate (&$checkobj, $infos)
602
  {
603
    $var = $checkobj->name.'_toMigrate';
604
    /* Fix displayed dn syntax */
605
606
607
    $infos['entries'] = $this->$var;
    foreach ($infos['entries'] as $key => $data) {
      $infos['entries'][$key]['dn'] = LDAP::fix($data['dn']);
608
    }
609
    $this->openDialog(new StepMigrateDialog($checkobj, 'setup_migrate_gosaAccounts.tpl', $infos));
610
611
  }

612
  function check_multipleGeneric_migrate_refresh (&$checkobj, $infos)
613
614
615
  {
    if (isset($_POST['dialog_showchanges'])) {
      /* Show changes */
616
617
      $fnc = 'check_'.$checkobj->name.'_migrate_confirm';
      $this->$fnc($checkobj, TRUE);
618
619
    } else {
      /* Hide changes */
620
      $checkobj->run();
621
622
    }
    /* Fix displayed dn syntax */
623
    $var = $checkobj->name.'_toMigrate';
624
625
626
    $infos['entries'] = $this->$var;
    foreach ($infos['entries'] as $key => $data) {
      $infos['entries'][$key]['dn'] = LDAP::fix($data['dn']);
627
    }
628
    return $infos;
629
  }
630

631
  function check_multipleGeneric_migrate_confirm(&$checkobj, $oc, $mandatory, $only_ldif)
632
  {
633
634
    global $config;
    $ldap = $config->get_ldap_link();
635

636
    /* Add objectClasses to the selected entries */
637
    $var = $checkobj->name.'_toMigrate';
638
639
640
    foreach ($this->$var as $key => &$entry) {
      $entry['checked'] = isset($_POST['migrate_'.$key]);
      if ($entry['checked']) {
641
        /* Get old objectClasses */
642
643
        $ldap->cat($entry['dn'], array_merge(array('objectClass'), array_keys($mandatory)));
        $attrs = $ldap->fetch();
644
645
646

        /* Create new objectClass array */
        $new_attrs  = array();
647
        $new_attrs['objectClass'] = $oc;
648
649
        for ($i = 0; $i < $attrs['objectClass']['count']; $i++) {
          if (!in_array_ics($attrs['objectClass'][$i], $new_attrs['objectClass'])) {
650
651
652
653
654
655
656
657
            $new_attrs['objectClass'][] = $attrs['objectClass'][$i];
          }
        }

        /* Append mandatories if missing */
        foreach ($mandatory as $name => $value) {
          if (!isset($attrs[$name])) {
            $new_attrs[$name] = $value;
658
659
660
          }
        }

661
662
        /* Set info attributes for current object,
         *  or write changes to the ldap database
663
         */
664
        if ($only_ldif) {
665
666
          $entry['before'] = $this->array_to_ldif($attrs);
          $entry['after']  = $this->array_to_ldif($new_attrs);
667
        } else {
668
          $ldap->cd($attrs['dn']);
669
          if (!$ldap->modify($new_attrs)) {
670
671
672
673
674
675
676
677
            msg_dialog::display(
              _('Migration error'),
              sprintf(
                _('Cannot migrate entry "%s":').'<br/><br/><i>%s</i>',
                LDAP::fix($attrs['dn']), $ldap->get_error()
              ),
              ERROR_DIALOG
            );
678
            return FALSE;
679
680
681
682
          }
        }
      }
    }
683
    unset($entry);
684
    return TRUE;
685
686
  }

687
  /* Check Acls if there is at least one object with acls defined */
688
  function check_adminAccount(&$checkobj)
689
  {
690
691
    global $config;

692
    /* Reset settings */
693
    $FD_1_0_8_found = FALSE;
694
695

    /* Establish ldap connection */
696
697
698
    $ldap = $config->get_ldap_link();
    $ldap->cd($config->current['BASE']);
    $res = $ldap->cat($config->current['BASE']);
699

700
    if (!$res) {
701
702
703
704
      throw new CheckFailedException(
        _('LDAP query failed'),
        _('Possibly the "root object" is missing.')
      );
705
    } else {
706
707
      $FD_1_0_8_found = FALSE;
      $FD_1_0_7_found = FALSE;
708
709
710

      $attrs = $ldap->fetch();

711
      /* Collect a list of available FusionDirectory users and groups */
712
      $users = array();
713
      $ldap->search('(objectClass=inetOrgPerson)', array('uid','dn'));
714
      while ($user_attrs = $ldap->fetch()) {
715
716
717
718
        $users[$user_attrs['dn']] = $user_attrs['uid'][0];
        $rusers[$user_attrs['uid'][0]] = $user_attrs['dn'];
      }
      $groups = array();
719
720
      $ldap->search("objectClass=posixGroup", array("cn","dn"));
      while ($group_attrs = $ldap->fetch()) {
721
722
        $groups[$group_attrs['dn']] = $group_attrs['cn'][0];
      }
723

724
      /* Check if a valid FusionDirectory 1.0.8 admin exists
725
726
          -> gosaAclEntry for an existing and accessible user.
       */
727
      $valid_users  = "";
728
      $valid_groups = "";
729
      if (isset($attrs['gosaAclEntry'])) {
730
        $acls = $attrs['gosaAclEntry'];
731
        for ($i = 0; $i < $acls['count']; $i++) {
732
          $acl = $acls[$i];
733
          $tmp = explode(":", $acl);
734

Côme Bernigaud's avatar
Côme Bernigaud committed
735
          if ($tmp[1] == "subtree") {
736
            /* Check if acl owner is a valid FusionDirectory user account */
737
738
739
740
741
742
743
744
            $ldap->cat(base64_decode($tmp[2]), array("gosaAclTemplate"), '(gosaAclTemplate=*:all;cmdrw)');
            if ($ldap->count()) {
              $members = explode(",", $tmp[3]);
              foreach ($members as $member) {
                $member = base64_decode($member);

                if (isset($users[$member])) {
                  $valid_users    .= $users[$member].", ";
745
                  $FD_1_0_8_found = TRUE;
746
747
748
749
750
751
752
753
754
                }
                if (isset($groups[$member])) {
                  $ldap->cat($member);
                  $group_attrs = $ldap->fetch();
                  $val_users = "";
                  if (isset($group_attrs['memberUid'])) {
                    for ($e = 0; $e < $group_attrs['memberUid']['count']; $e ++) {
                      if (isset($rusers[$group_attrs['memberUid'][$e]])) {
                        $val_users .= $group_attrs['memberUid'][$e].", ";
755
756
757
                      }
                    }
                  }
758
759
760
761
                  if (!empty($val_users)) {
                    $valid_groups .= $groups[$member]."(<i>".trim($val_users, ", ")."</i>), ";
                    $FD_1_0_8_found  = TRUE;
                  }
762
763
764
765
766
767
768
                }
              }
            }
          }
        }
      }

769
      /* Try to find an old FD 1.0.7 administrator account that may be migrated */
770
      if (!$FD_1_0_8_found) {
771
        $valid_users  = "";
772
        $valid_groups = "";
773
774
775
776
777
778
779
780
781
782
783
784
785
        if (isset($attrs['gosaAclEntry'])) {
          $acls = $attrs['gosaAclEntry'];
          for ($i = 0; $i < $acls['count']; $i++) {
            $acl = $acls[$i];
            $tmp = explode(":", $acl);

            if ($tmp[1] == "psub") {
              $members = explode(",", $tmp[2]);
              foreach ($members as $member) {
                $member = base64_decode($member);
                if (isset($users[$member])) {
                  if (preg_match("/all;cmdrw/i", $tmp[3])) {
                    $valid_users    .= $users[$member].", ";
786
                    $FD_1_0_7_found = TRUE;
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
                  }
                }
                if (isset($groups[$member])) {
                  if (preg_match("/all;cmdrw/i", $tmp[3])) {
                    $ldap->cat($member);
                    $group_attrs = $ldap->fetch();
                    $val_users = "";
                    if (isset($group_attrs['memberUid'])) {
                      for ($e = 0; $e < $group_attrs['memberUid']['count']; $e++) {
                        if (isset($rusers[$group_attrs['memberUid'][$e]])) {
                          $val_users .= $group_attrs['memberUid'][$e].", ";
                        }
                      }
                    }
                    if (!empty($val_users)) {
                      $valid_groups .= $groups[$member]."(<i>".trim($val_users, ", ")."</i>), ";
                      $FD_1_0_7_found  = TRUE;
                    }
                  }
                }
              }
            } elseif ($tmp[1] == "role") {
              /* Check if acl owner is a valid FusionDirectory user account */
              $ldap->cat(base64_decode($tmp[2]), array("gosaAclTemplate"));
              $ret = $ldap->fetch();

              if (isset($ret['gosaAclTemplate'])) {
                $cnt = $ret['gosaAclTemplate']['count'];
                for ($e = 0; $e < $cnt; $e++) {

                  $a_str = $ret['gosaAclTemplate'][$e];
                  if (preg_match("/^[0-9]*:psub:/", $a_str) && preg_match("/:all;cmdrw$/", $a_str)) {

                    $members = explode(",", $tmp[3]);
                    foreach ($members as $member) {
                      $member = base64_decode($member);

                      if (isset($users[$member])) {
                        $valid_users    .= $users[$member].", ";
826
                        $FD_1_0_7_found = TRUE;
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
                      }
                      if (isset($groups[$member])) {
                        $ldap->cat($member);
                        $group_attrs = $ldap->fetch();
                        $val_users = "";
                        if (isset($group_attrs['memberUid'])) {
                          for ($e = 0; $e < $group_attrs['memberUid']['count']; $e ++) {
                            if (isset($rusers[$group_attrs['memberUid'][$e]])) {
                              $val_users .= $group_attrs['memberUid'][$e].", ";
                            }
                          }
                        }
                        if (!empty($val_users)) {
                          $valid_groups .= $groups[$member]."(<i>".trim($val_users, ", ")."</i>), ";
                          $FD_1_0_7_found  = TRUE;
                        }
                      }
                    }
                  }
                }
847
              }
848
            }
849
850
851
852
          }
        }
      }

853
      /* Print out results */
854
      if ($FD_1_0_7_found) {
855
        $str = "";
856
857
858
        if (!empty($valid_users)) {
          $str .= '<i>'.sprintf(_('FD 1.0.7 administrative accounts found: %s'), trim($valid_users, ', ')).'</i><br/>';
        }
859
        if (!empty($valid_groups)) {
860
          $str .= '<i>'.sprintf(_('FD 1.0.7 administrative groups found: %s'), trim($valid_groups, ', ')).'</i><br/>';
861
        }
862
863
864
865
866
867
        $str .= _('You may run <i>fusiondirectory-setup --migrate-acls</i> after saving config file at the end of the setup to migrate it.<br/>');
        throw new CheckFailedException(
          _('Failed'),
          $str._('There is no valid FusionDirectory 1.0.8 administrator account inside your LDAP.').'&nbsp;'.
          $checkobj->submit(_('Create'), 'create')
        );
868
      } elseif ($FD_1_0_8_found) {
869
        $str = "";
870
871
        if (!empty($valid_users)) {
          $str .= "<b>"._("Users")."</b>:&nbsp;".trim($valid_users, ", ")."<br>";
872
        }
873
874
        if (!empty($valid_groups)) {
          $str .= "<b>"._("Groups")."</b>:&nbsp;".trim($valid_groups, ", ")."<br>";
875
        }
876
        return $str;
877
      } else {
878
879
880
881
882
        throw new CheckFailedException(
          _('Failed'),
          _('There is no FusionDirectory administrator account inside your LDAP.').'&nbsp;'.
          $checkobj->submit(_('Create'), 'create')
        );
883
884
885
886
      }
    }

    // Reload base OC
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
    $this->checks['baseOC']->run();
    return '';
  }

  function check_adminAccount_create(&$checkobj)
  {
    $infos = array(
      'uid'       => 'fd-admin',
      'password'  => '',
      'password2' => '',
    );
    $this->openDialog(new StepMigrateDialog($checkobj, 'setup_migrate_adminAccount.tpl', $infos));
  }

  function check_adminAccount_migrate_confirm(&$checkobj)
  {
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
    global $config;
    session::global_set('CurrentMainBase', $config->current['BASE']);

    /* Creating role */
    $ldap = $config->get_ldap_link();

    $ldap->cd($config->current['BASE']);
    $ldap->search('(&(objectClass=gosaRole)(gosaAclTemplate=*:all;cmdrw))', array('dn'));
    if ($attrs = $ldap->fetch()) {
      $roledn = $attrs['dn'];
    } else {
      $tabObject  = objects::create('aclRole');
      $baseObject = $tabObject->getBaseObject();

      $baseObject->cn               = 'admin';
      $baseObject->description      = _('Gives all rights on all objects');
      $baseObject->gosaAclTemplate  = array(array('all' => array('0' => 'cmdrw')));

      $tabObject->save();
      $roledn = $tabObject->dn;
    }

    /* Creating user */
926
927
928
929
930
931
932
933
934
935
936
937
938
939
    $tabObject = objects::create('user');
    $_POST['givenName']                   = 'System';
    $_POST['sn']                          = 'Administrator';
    $_POST[$tabObject->current.'_posted'] = TRUE;
    $_POST['dialog_refresh']              = TRUE;
    $tabObject->save_object();
    $errors = $tabObject->check();
    if (!empty($errors)) {
      foreach ($errors as $error) {
        msg_dialog::display(_('Error'), $error, ERROR_DIALOG);
      }
      return FALSE;
    }
    $tabObject->save();
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
    $admindn = $tabObject->dn;

    /* Assigning role */
    $tabObject  = objects::open($config->current['BASE'], 'aclAssignment');
    $baseObject = $tabObject->getBaseObject();

    $assignments = $baseObject->gosaAclEntry;
    array_unshift(
      $assignments,
      array(
        'scope'   => 'subtree',
        'role'    => $roledn,
        'members' => array($admindn),
      )
    );
    $baseObject->gosaAclEntry = $assignments;
    $tabObject->save();

958
959
960
961
962
963
964
965
966
967
    return TRUE;
  }

  function check_adminAccount_migrate_refresh(&$checkobj)
  {
    return array(
      'uid'       => $_POST['uid'],
      'password'  => $_POST['userPassword_password'],
      'password2' => $_POST['userPassword_password2'],
    );
968
969
  }

970
  /* Check if default roles and groupes have been inserted */
971
  function check_defaultACLs(&$checkobj)
972
  {
973
974
975
976
    global $config;
    $ldap = $config->get_ldap_link();
    $ldap->cd($config->current['BASE']);
    $res = $ldap->cat($config->current['BASE']);
977
978

    if (!$res) {
979
980
981
982
      throw new CheckFailedException(
        _('LDAP query failed'),
        _('Possibly the "root object" is missing.')
      );
983
984
985
986
    }

    $existings = 0;
    foreach ($this->defaultRoles as $role) {
987
      $dn = 'cn='.$role['cn'].','.get_ou('aclRoleRDN').$config->current['BASE'];
988
989
990
991
992
      $ldap->cat($dn, array('dn'));
      if ($ldap->count() > 0) {
        $existings++;
      }
    }
993
    $status = ($existings == count($this->defaultRoles));
994
    if ($existings == 0) {
995
      $checkobj->msg = _('Default ACL roles have not been inserted');
996
    } elseif ($existings < count($this->defaultRoles)) {
997
998
999
1000
1001
1002
1003
1004
1005
      $checkobj->msg = _('Some default ACL roles are missing');
    } else {
      $checkobj->msg = _('Default ACL roles have been inserted');
    }
    if ($status === FALSE) {
      throw new CheckFailedException(
        $checkobj->msg,
        '&nbsp;'.$checkobj->submit()
      );
1006
    } else {
1007
      return '';
1008
    }
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
  }

  function check_defaultACLs_migrate(&$checkobj)
  {
    global $config;
    $ldap = $config->get_ldap_link();
    $ldap->cd($config->current['BASE']);

    foreach ($this->defaultRoles as $role) {
      $dn = 'cn='.$role['cn'].','.get_ou('aclRoleRDN').$config->current['BASE'];
      $ldap->cat($dn);
      if ($ldap->count() == 0) {
1021
1022
        $ldap->cd($config->current['BASE']);
        $ldap->create_missing_trees(get_ou('aclRoleRDN').$config->current['BASE']);
1023
1024
1025
1026
1027
1028
1029
        $ldap->cd($dn);
        $ldap->add($role);
        if (!$ldap->success()) {
          msg_dialog::display(
            _('Migration error'),
            sprintf(
              _('Cannot add ACL role "%s":').'<br/><br/><i>%s</i>',
1030
              LDAP::fix($dn), $ldap->get_error()
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
            ),
            ERROR_DIALOG
          );
          return FALSE;
        }
      }
    }
    $checkobj->run();
    return TRUE;
  }

  /* Search for users outside the people ou */
  function check_outsideUsers(&$checkobj)
  {
    global $config;
    $ldap = $config->get_ldap_link();

    $ldap->cd($config->current['BASE']);

    /***********
     * Search for all users
     ***********/
1053
    $res = $ldap->search('(&(objectClass=inetOrgPerson)(!(uid=*$)))', array('dn'));
1054
1055
1056
1057
1058
1059
1060
1061
    if (!$res) {
      throw new CheckFailedException(
        _('LDAP query failed'),
        _('Possibly the "root object" is missing.')
      );
    }

    /***********
1062
     * Check if returned users are within a valid department. (peopleou,gosaDepartment,base)
1063
     ***********/
1064
    $this->outsideUsers_toMigrate = array();
1065
1066
1067
    $people_ou = trim(get_ou('userRDN'));

    while ($attrs = $ldap->fetch()) {
1068
      $people_db_base = preg_replace('/^[^,]+,'.preg_quote($people_ou, '/').'/i', '', $attrs['dn']);
1069
1070
1071
1072

      /* Check if entry is not an addressbook only user
       *  and verify that he is in a valid department
       */
1073
1074
1075
1076
      if (!preg_match('/dc=addressbook,/', $people_db_base) &&
          !in_array($people_db_base, $config->departments)) {
        $attrs['checked'] = FALSE;
        $attrs['ldif']    = '';
1077
        $this->outsideUsers_toMigrate[base64_encode($attrs['dn'])] = $attrs;
1078
1079
1080
      }
    }

1081
    if (count($this->outsideUsers_toMigrate)) {
1082
1083
      throw new CheckFailedException(
        "<div style='color:#F0A500'>"._("Warning")."</div>",
1084
        sprintf(_('Found %s user(s) outside the configured tree "%s".'), count($this->outsideUsers_toMigrate), $people_ou).
1085
1086
1087
1088
1089
1090
1091
        $checkobj->submit()
      );
    } else {
      return '';
    }
  }

1092
1093
1094
1095
1096
1097
  function check_outsideUsers_migrate(&$checkobj)
  {
    global $config;
    $this->check_multipleGeneric_migrate(
      $checkobj,
      array(
1098
1099
1100
1101
        'title'       => _('Move users into configured user tree'),
        'outside'     => TRUE,
        'ous'         => $config->departments,
        'destination' => $_POST['destination'],
1102
1103
1104
1105
1106
1107
      )
    );
  }

  function check_outsideUsers_migrate_refresh(&$checkobj)
  {
1108
    global $config;
1109
1110
1111
    return $this->check_multipleGeneric_migrate_refresh(
      $checkobj,
      array(
1112
1113
1114
1115
        'title'       => _('Move users into configured user tree'),
        'outside'     => TRUE,
        'ous'         => $config->departments,
        'destination' => $_POST['destination'],
1116
1117
1118
1119
      )
    );
  }

1120
  function check_outsideUsers_migrate_confirm(&$checkobj, $only_ldif = FALSE, $ou = 'userRDN')
1121
1122
1123
1124
1125
1126
1127
  {
    global $config;
    $ldap = $config->get_ldap_link();
    $ldap->cd($config->current['BASE']);

    /* Check if there was a destination department posted */
    if (isset($_POST['destination'])) {
1128
      $destination_dep = get_ou($ou).$_POST['destination'];
1129
1130
1131
1132
1133
    } else {
      msg_dialog::display(_('LDAP error'), _('Cannot move entries to the requested department!'), ERROR_DIALOG);
      return FALSE;
    }

1134
1135
1136
1137
1138
    $var = $checkobj->name.'_toMigrate';
    foreach ($this->$var as $b_dn => &$entry) {
      $entry['checked'] = isset($_POST['migrate_'.$b_dn]);
      $entry['ldif']    = '';
      if ($entry['checked']) {
1139
        $dn = base64_decode($b_dn);
1140
        $d_dn = preg_replace('/,.*$/', ','.$destination_dep, $dn);
1141
        if ($only_ldif) {
1142
          $entry['ldif'] = _('Entry will be moved from').":<br/>\t".($ldap->fix($dn)).'<br/>'._('to').":<br/>\t".($ldap->fix($d_dn));
1143
1144
1145
1146
1147
1148

          /* Check if there are references to this object */
          $ldap->search('(&(member='.LDAP::prepare4filter($dn).')(|(objectClass=gosaGroupOfNames)(objectClass=groupOfNames)))', array('dn'));
          $refs = '';
          while ($attrs = $ldap->fetch()) {
            $ref_dn = $attrs['dn'];
1149
            $refs .= "<br/>\t".$ref_dn;
1150
1151
          }
          if (!empty($refs)) {
1152
            $entry['ldif'] .= '<br/><br/><i>'._('The following references will be updated').':</i>'.$refs;
1153
1154
1155
1156
1157
1158
          }
        } else {
          $this->move($dn, $d_dn);
        }
      }
    }
1159
1160
1161
    unset($entry);

    return TRUE;
1162
1163
  }

1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
  /* Search for groups outside the group ou */
  function check_outsideGroups(&$checkobj)
  {
    global $config;
    $ldap = $config->get_ldap_link();

    $group_ou = get_ou('groupRDN');
    $ldap->cd($config->current['BASE']);

    /***********
     * Get all groups
     ***********/
1176
    $res = $ldap->search('(objectClass=posixGroup)', array('dn'));
1177
1178
1179
1180
1181
1182
1183
    if (!$res) {
      throw new CheckFailedException(
        _('LDAP query failed'),
        _('Possibly the "root object" is missing.'