Merge pull request #54 from LDAPAccountManager/importexport

Importexport
This commit is contained in:
gruberroland 2018-10-14 09:57:53 +02:00 committed by GitHub
commit 65de9a2d18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2721 additions and 19 deletions

View File

@ -1,3 +1,7 @@
December 2018 6.6
- New import/export in tools menu
25.09.2018 6.5
- Password change possible via LDAP EXOP operation (set LDAP_EXOP as password hash, requires PHP 7.2)
- Support Imagick and GD
@ -25,6 +29,7 @@
- Fixed bugs:
-> Error on password reset page when custom fields is used (194)
19.03.2018 6.3
- Server profile: added option if referential integrity overlay is active to skip cleanup actions
- Unix: several options are now specific to subaccount types (reconfiguration required!)

View File

@ -5880,9 +5880,6 @@ OK (10 msec)</programlisting>
<para>There are also some special functions available:</para>
<para><emphasis role="bold">Export:</emphasis> This allows you to export
entries to a file (e.g. LDIF or CSV format).</para>
<para><emphasis role="bold">Show internal attributes:</emphasis> Shows
internal attributes of the current entry. This includes information about
the creator and creation time of the entry.</para>

View File

@ -74,14 +74,16 @@
</screenshot>
</section>
<section>
<section id="tool_upload">
<title>File upload</title>
<para>When you need to create lots of accounts then you can use LAM's file
upload to create them. LAM will read a CSV formatted file and create the
related LDAP entries. Please check the data in you CSV file carefully. LAM
will do less checks for the file upload than for single account
creation.</para>
upload to create them. In contrast to <link linkend="tool_upload">LDAP
import/export</link> this operates on account type level.</para>
<para>LAM will read a CSV formatted file and create the related LDAP
entries. Please check the data in you CSV file carefully. LAM will do less
checks for the file upload than for single account creation.</para>
<para>At the first page please select the account type and what extensions
should be activated.</para>
@ -201,6 +203,66 @@
</screenshot>
</section>
<section id="tool_importexport">
<title>LDAP import/export</title>
<para>Here you can import and export plain LDAP data. In contrast to <link
linkend="tool_upload">file upload</link> this operates on plain LDAP
attribute level.</para>
<section>
<title>Import</title>
<para>The LDAP import supports input data in <ulink
url="https://en.wikipedia.org/wiki/LDAP_Data_Interchange_Format">LDIF</ulink>
format. You can provide plain text or upload an LDIF file.</para>
<para>The "Don't stop on errors" option will cause the import to
continue even if entries could not be created.</para>
<screenshot>
<graphic fileref="images/tool_import.png"/>
</screenshot>
</section>
<section>
<title>Export</title>
<para>Here you can export your plain LDAP data as LDIF or CSV
file.</para>
<screenshot>
<graphic fileref="images/tool_export.png"/>
</screenshot>
<para>Base DN: this is the starting point of the export. Enter a DN or
press the magnifying glass icon to open the DN selection dialog.</para>
<para>Search scope: You can export just the base DN, base DN + its
direct children or the whole subtree.</para>
<para>Search filter: this can be used to filter the entries by
specifying a standard LDAP filter. The preselected filter
"(objectclass=*)" matches all entries.</para>
<para>Attributes: the list of attributes that should be part of export.
"*" matches all standard attributes (excluding system
attributes).</para>
<para>Include system attributes: this will also include system
attributes like the entry creation time and creator's DN.</para>
<para>Save as file: will save to file instead of printing the data on
the web page.</para>
<para>Export format: you can select LDIF or CSV (e.g. for usage in
spreadsheet applications).</para>
<para>End of line: use the one appropriate for your operating
system.</para>
</section>
</section>
<section>
<title>OU editor</title>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

View File

@ -330,7 +330,7 @@ $helpArray = array (
"Text" => _("This will create a new organisational unit under the selected one.")),
"602" => array ("Headline" => _("OU-Editor") . " - " . _("Delete organisational unit"),
"Text" => _("This will delete the selected organisational unit. The OU has to be empty.")),
// 700 - 799
// 700 - 749
// multi edit tool
"700" => array ("Headline" => _('LDAP suffix'),
"Text" => _('Please select the suffix where changes should be done.')),
@ -338,6 +338,18 @@ $helpArray = array (
"Text" => _('Use this to enter an additional LDAP filter (e.g. "(cn!=admin)") to reduce the number of entries to modify.')),
"702" => array ("Headline" => _('Operations'),
"Text" => _('Please specify which attributes should be changed. The modify operation will also add an value if the attribute does not yet exist. To delete all values of an attribute please leave the value field empty.')),
// 750 - 799
// import/export
"750" => array ("Headline" => _('LDIF data'),
"Text" => _('The input data must be formatted in LDIF format.')),
"751" => array ("Headline" => _('Base DN'),
"Text" => _('The export will read entries of this DN.')),
"752" => array ("Headline" => _('Search filter'),
"Text" => _('Please enter an LDAP filter to specifiy the exported entries.')),
"753" => array ("Headline" => _('Attributes'),
"Text" => _('Please enter a comma separated list of attributes to export. Using "*" will export all attributes.')),
"754" => array ("Headline" => _('Include system attributes'),
"Text" => _('Activate this option to export internal attributes that are not visible by default.')),
// 800 - 899
// jobs
'800' => array(

View File

@ -854,6 +854,32 @@ function ldapGetDN($dn, $attributes = array('dn'), $handle = null) {
return $return;
}
/**
* Returns the DN and children of a given DN.
*
* @param String $dn DN
* @param String $filter LDAP filter
* @param array $attributes list of attributes to fetch
* @param handle $handle LDAP handle (optional for admin interface pages)
* @return array attributes or null if not found
*/
function ldapListDN($dn, $filter = '(objectclass=*)', $attributes = array('dn'), $handle = null) {
if ($handle == null) {
$handle = $_SESSION['ldap']->server();
}
$return = null;
$sr = @ldap_list($handle, escapeDN($dn), $filter, $attributes, 0, 0, 0, LDAP_DEREF_NEVER);
if ($sr) {
$entries = ldap_get_entries($handle, $sr);
if ($entries) {
cleanLDAPResult($entries);
$return = $entries;
}
@ldap_free_result($sr);
}
return $return;
}
/**
* Deletes a DN and all child entries.
*

321
lam/lib/export.inc Normal file
View File

@ -0,0 +1,321 @@
<?php
namespace LAM\TOOLS\IMPORT_EXPORT;
use \htmlStatusMessage;
use \LAMException;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2018 Roland Gruber
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* LDIF export.
*
* @author Roland Gruber
* @package tools
*/
/** LDAP handle */
include_once('ldap.inc');
/**
* Creates LDAP accounts for file upload.
*
* @author Roland Gruber
* @package tools
*/
class Exporter {
const DATA = 'data';
const STATUS = 'status';
const FILE = 'file';
const OUTPUT = 'output';
private $baseDn = null;
private $searchScope = null;
private $filter = null;
private $attributes = null;
private $includeSystem = false;
private $saveAsFile = false;
private $format = null;
private $ending = null;
/**
* Constructor.
*
* @param string $baseDn base DN
* @param string $searchScope search scope (base, one, sub)
* @param string $filter search filter
* @param string $attributes attributes to return
* @param bool $includeSystem include system attributes
* @param bool $saveAsFile return as file
* @param string $format output format (LDIF, CSV)
* @param string $ending line endings (windows, unix)
*/
public function __construct($baseDn, $searchScope, $filter, $attributes, $includeSystem, $saveAsFile, $format, $ending) {
$this->baseDn = $baseDn;
$this->searchScope = $searchScope;
$this->filter = $filter;
$this->attributes = $attributes;
$this->includeSystem = $includeSystem;
$this->saveAsFile = $saveAsFile;
$this->format = $format;
$this->ending = $ending;
}
/**
* Starts the export process.
*
* @return string JSON result
*/
public function doExport() {
try {
$this->checkParameters();
$results = $this->getLDAPData();
return $this->writeDataAndReturnJson($results);
}
catch (LAMException $e) {
$data = Exporter::formatMessage('ERROR', $e->getTitle(), $e->getMessage());
$status = array(
Exporter::STATUS => 'failed',
Exporter::DATA => $data
);
return json_encode($status);
}
}
/**
* Returns the HTML for an error message.
*
* @param string $type message type (e.g. INFO)
* @param string $title title
* @param string $message message
* @return string HTML
*/
public static function formatMessage($type, $title, $message) {
$msg = new htmlStatusMessage($type, $title, $message);
$tabindex = 0;
ob_start();
$msg->generateHTML(null, array($msg), array(), true, $tabindex, 'user');
$data = ob_get_contents();
ob_clean();
return $data;
}
/**
* Checks the input parameters for validity.
*
* @throws LAMException in case of errors
*/
private function checkParameters() {
if (!get_preg($this->baseDn, 'dn')) {
throw new LAMException(_('Please enter a valid DN in the field:'), _('Base DN'));
}
}
/**
* Returns the LDAP entries
*
* @return array[] LDAP entries
*/
private function getLDAPData() {
$attributes = preg_split('/,[ ]*/', $this->attributes);
if ($this->includeSystem) {
$attributes = array_merge($attributes, array('+', 'passwordRetryCount', 'accountUnlockTime', 'nsAccountLock',
'nsRoleDN', 'passwordExpirationTime', 'pwdChangedTime'));
}
$attributes = array_unique($attributes);
switch ($this->searchScope) {
case 'base':
return array(ldapGetDN($this->baseDn, $attributes));
break;
case 'one':
return ldapListDN($this->baseDn, $this->filter, $attributes);
break;
case 'sub':
return searchLDAP($this->baseDn, $this->filter, $attributes);
break;
default:
throw new LAMException('Invalid scope');
break;
}
}
/**
* Writes the entries to file/response and prints JSON.
*
* @param array $entries LDAP entries
*/
private function writeDataAndReturnJson(&$entries) {
$lineEnding = ($this->ending === 'windows') ? "\r\n" : "\n";
if ($this->format === 'csv') {
$output = $this->getCsvOutput($entries, $lineEnding);
}
elseif ($this->format === 'ldif') {
$output = $this->getLdifOutput($entries, $lineEnding);
}
else {
throw new LAMException('Invalid format');
}
if ($this->saveAsFile) {
$filename = '../../tmp/' . getRandomNumber() . time() .'.' . $this->format;
$handle = fopen($filename, 'w');
chmod($filename, 0640);
fwrite($handle, $output);
fclose($handle);
return json_encode(array(
Exporter::FILE => $filename,
Exporter::STATUS => 'done'
));
}
return json_encode(array(
Exporter::OUTPUT => htmlspecialchars($output, ENT_NOQUOTES),
Exporter::STATUS => 'done'
));
}
/**
* Converts the given LDAP entries to CSV format.
*
* @param string $entries entries
* @param string $lineEnding line ending
*/
private function getCsvOutput(&$entries, $lineEnding) {
$attributeNames = array();
foreach ($entries as $entry) {
$entryAttributeNames = array_keys($entry);
foreach ($entryAttributeNames as $name) {
if (!in_array($name, $attributeNames)) {
$attributeNames[] = $name;
}
}
}
$attributeNames = array_delete(array('dn'), $attributeNames);
sort($attributeNames);
array_unshift($attributeNames, 'dn');
$attributeNamesQuoted = array_map(array($this, 'escapeCsvAndAddQuotes'), $attributeNames);
$output = '';
// header
$output .= implode(',', $attributeNamesQuoted) . $lineEnding;
// content
foreach ($entries as $entry) {
$values = array();
foreach ($attributeNames as $name) {
if (!isset($entry[$name])) {
$values[] = $this->escapeCsvAndAddQuotes('');
}
elseif (is_array($entry[$name])) {
$values[] = $this->escapeCsvAndAddQuotes(implode(' | ', $entry[$name]));
}
else {
$values[] = $this->escapeCsvAndAddQuotes($entry[$name]);
}
}
$output .= implode(',', $values) . $lineEnding;
}
return $output;
}
/**
* Escapes a CSV value and adds quotes arround it.
*
* @param string $value CSV value
* @return string escaped and quoted value
*/
private function escapeCsvAndAddQuotes($value) {
return '"' . str_replace('"', '""', $value) . '"';
}
/**
* Converts the given LDAP entries to LDIF format.
*
* @param string $entries entries
* @param string $lineEnding line ending
*/
private function getLdifOutput(&$entries, $lineEnding) {
$output = '';
$output .= '#' . $lineEnding;
$output .= '# ' . _('Base DN') . ': ' . $this->baseDn . $lineEnding;
$output .= '# ' . _('Search scope') . ': ' . $this->searchScope . $lineEnding;
$output .= '# ' . _('Search filter') . ': ' . $this->filter . $lineEnding;
$output .= '# ' . _('Total entries') . ': ' . sizeof($entries) . $lineEnding;
$output .= '#' . $lineEnding;
$output .= '# Generated by LDAP Account Manager on ' . date('Y-m-d H:i:s') . $lineEnding;
$output .= $lineEnding;
$output .= $lineEnding;
$output .= 'version: 1';
$output .= $lineEnding;
$output .= $lineEnding;
foreach ($entries as $entry) {
$output .= 'dn: ' . $entry['dn'] . $lineEnding;
unset($entry['dn']);
ksort($entry);
foreach ($entry as $attributeName => $values) {
foreach ($values as $value) {
if ($this->isPlainAscii($value)) {
$output .= $this->wrapLdif($attributeName . ': ' . $value, $lineEnding) . $lineEnding;
}
else {
$output .= $this->wrapLdif($attributeName . ':: ' . base64_encode($value), $lineEnding) . $lineEnding;
}
}
}
$output .= $lineEnding;
}
return $output;
}
/**
* Splits the LDIF line if needed.
*
* @param string $content line content
* @param string $lineEnding line ending
*/
private function wrapLdif($content, $lineEnding) {
$line_length = 76;
if (strlen($content) <= $line_length) {
return $content;
}
$wrappedContent = substr($content, 0, $line_length) . $lineEnding;
$contentLeft = substr($content, $line_length);
$line_length = $line_length - 1;
$lines = str_split($contentLeft, $line_length);
foreach ($lines as $line) {
$wrappedContent .= ' ' . $line . $lineEnding;
}
return trim($wrappedContent);
}
/**
* Checks if the value is plain ASCII.
*
* @param string $content content to check
* @return bool is plain ASCII
*/
private function isPlainAscii($content) {
for ($i=0; $i < strlen($content); $i++) {
if (ord($content[$i]) < 32 || ord($content[$i]) > 127) {
return false;
}
}
return true;
}
}

View File

@ -472,6 +472,8 @@ class htmlInputField extends htmlElement {
protected $autocompleteMinLength = 1;
/** show calendar */
protected $showCalendar = false;
/** show DN selection */
protected $showDnSelection = false;
/** calendar format */
protected $calendarFormat = '';
/** title attribute */
@ -589,8 +591,19 @@ class htmlInputField extends htmlElement {
if (!empty($this->title)) {
$title = ' title="' . $this->title . '"';
}
if ($this->showDnSelection) {
echo '<span class="nowrap">';
}
echo '<input type="' . $inputType . '"' . $class . $name . $id . $value . $maxLength
. $min . $max . $size . $fieldTabIndex . $onKeyPress . $onKeyUp . $title . $disabled . '>';
if ($this->showDnSelection) {
echo '<img class="align-middle" src="../../graphics/view.png"
width="16" height="16" title="' . _('Choose entry') . '"
onclick="window.lam.html.showDnSelection(\'' . $this->fieldName . '\', \'' . _('Choose entry') . '\'
, \'' . _('Ok') . '\', \'' . _('Cancel') . '\', \'' . getSecurityTokenName() . '\'
, \'' . getSecurityTokenValue() . '\');">';
echo '</span>';
}
// autocompletion
if ($this->autocomplete) {
echo "<script type=\"text/javascript\">\n";
@ -805,6 +818,13 @@ class htmlInputField extends htmlElement {
$this->calendarFormat = $format;
}
/**
* Shows a DN selection next to input field.
*/
public function showDnSelection() {
$this->showDnSelection = true;
}
/**
* Sets the title for the input field.
*
@ -1623,6 +1643,10 @@ class htmlRadio extends htmlElement {
private $isEnabled = true;
/** on change code */
private $onchangeEvent = null;
/** list of enclosing table rows to hide when checked */
protected $tableRowsToHide = array();
/** list of enclosing table rows to show when checked */
protected $tableRowsToShow = array();
/**
* Constructor.
@ -1663,13 +1687,23 @@ class htmlRadio extends htmlElement {
if (!$this->isEnabled) {
$disabled = ' disabled';
}
$onchange = '';
if ($this->onchangeEvent != null) {
$onchange = ' onchange="' . $this->onchangeEvent . '"';
if (($this->tableRowsToHide != null) || ($this->tableRowsToShow != null)) {
$this->printInitialState();
}
// print radio list
$counter = 0;
foreach ($this->elements as $label => $value) {
$showHideOnchange = '';
if (($this->tableRowsToHide != null) || ($this->tableRowsToShow != null)) {
$showHideOnchange = $this->getOnchangeCodeForShowHideTableRows($counter);
}
$onchange = '';
if ($this->onchangeEvent != null) {
$onchange = ' onchange="' . $this->onchangeEvent . '"';
}
elseif (!empty($showHideOnchange)) {
$onchange = ' onchange="' . $showHideOnchange . '"';
}
$onClick = 'onClick="
jQuery(\'input[name=' . $this->name . ']\').prop(\'checked\', false);
jQuery(\'#' . $this->name . $counter . '\').prop(\'checked\', true);
@ -1709,6 +1743,104 @@ class htmlRadio extends htmlElement {
$this->onchangeEvent = htmlspecialchars($onchangeEvent);
}
/**
* Returns the selector to use to find the show/hide elements.
*
* @return string selector
*/
protected function getShowHideSelector() {
return 'tr';
}
/**
* Creates the JavaScript code to hide/show table rows based on the select value.
*
* @param int $counter index
* @return String onChange code
*/
private function getOnchangeCodeForShowHideTableRows($counter) {
$onChange = '';
if ((sizeof($this->tableRowsToHide) == 0) && (sizeof($this->tableRowsToShow) == 0)) {
return;
}
$values = array();
if (!empty($this->tableRowsToHide)) {
$values = array_merge($values, array_keys($this->tableRowsToHide));
}
if (!empty($this->tableRowsToShow)) {
$values = array_merge($values, array_keys($this->tableRowsToShow));
}
$values = array_unique($values);
$selector = $this->getShowHideSelector();
// build Java script to show/hide depending fields
foreach ($values as $val) {
// build onChange listener
$onChange .= 'if (jQuery(\'#' . $this->name . $counter . '\').val() == \'' . $val . '\') {';
if (isset($this->tableRowsToShow[$val])) {
for ($i = 0; $i < sizeof($this->tableRowsToShow[$val]); $i++) {
$onChange .= 'jQuery(\'#' . $this->tableRowsToShow[$val][$i] . '\').closest(\'' . $selector . '\').removeClass(\'hidden\');';
}
}
if (isset($this->tableRowsToHide[$val])) {
for ($i = 0; $i < sizeof($this->tableRowsToHide[$val]); $i++) {
$onChange .= 'jQuery(\'#' . $this->tableRowsToHide[$val][$i] . '\').closest(\'' . $selector . '\').addClass(\'hidden\');';
}
}
$onChange .= '};';
}
return $onChange;
}
private function printInitialState() {
$selector = $this->getShowHideSelector();
// build script to set initial state
$script = '<script type="text/javascript">jQuery(document).ready(function() {' . "\n";
if (isset($this->tableRowsToShow[$this->selectedElement])) {
for ($i = 0; $i < sizeof($this->tableRowsToShow[$this->selectedElement]); $i++) {
$classType = 'removeClass';
$script .= 'jQuery(\'#' . $this->tableRowsToShow[$this->selectedElement][$i] . '\').closest(\'' . $selector . '\').' . $classType . '(\'hidden\');' . "\n";
}
}
if (isset($this->tableRowsToHide[$this->selectedElement])) {
for ($i = 0; $i < sizeof($this->tableRowsToHide[$this->selectedElement]); $i++) {
$classType = 'addClass';
$script .= 'jQuery(\'#' . $this->tableRowsToHide[$this->selectedElement][$i] . '\').closest(\'' . $selector . '\').' . $classType . '(\'hidden\');' . "\n";
}
}
$script .= '});</script>';
echo $script;
}
/**
* This will hide the given table rows when the radio is changed to the specified value.
* The given IDs can be of any e.g. input element. Starting from this element
* the first parent "<tr>" element will be used to show/hide.
* <br>
* <br>
* <br> Example for $tableRowsToHide:
* <br> array('val1' => array('option1', 'option2'), 'val2' => array('option3'))
*
* @param array $tableRowsToHide array of select value => array of IDs of child elements to hide
*/
public function setTableRowsToHide($tableRowsToHide) {
$this->tableRowsToHide = $tableRowsToHide;
}
/**
* This will show the given table rows when the radio is changed to the specified value.
* The given IDs can be of any e.g. input element. Starting from this element
* the first parent "<tr>" element will be used to show/hide.
* <br>
* <br>
* <br> Example for $tableRowsToShow:
* <br> array('val1' => array('option1', 'option2'), 'val2' => array('option3'))
*
* @param array $tableRowsToShow array of select value => array of IDs of child elements to show
*/
public function setTableRowsToShow($tableRowsToShow) {
$this->tableRowsToShow = $tableRowsToShow;
}
}
/**
@ -2292,7 +2424,7 @@ class htmlInputFileUpload extends htmlElement {
if (!$this->isEnabled) {
$disabled = ' disabled';
}
echo '<input type="file" name="' . $this->name . '"' . $tabindexValue . $disabled . '>';
echo '<input type="file" id="' . $this->name . '" name="' . $this->name . '"' . $tabindexValue . $disabled . '>';
return array($this->name => 'file');
}
@ -3828,7 +3960,7 @@ class htmlResponsiveRow extends htmlElement {
if ($this->id !== null) {
$idParam = ' id="' . $this->id . '"';
}
echo '<div class="row ' . $cssClasses . '"' . $idParam . '>';
echo '<div class="row ' . $cssClasses . '"' . $this->getDataAttributesAsString() . $idParam . '>';
foreach ($this->cells as $cell) {
$return = array_merge($return, $cell->generateHTML($module, $input, $values, $restricted, $tabindex, $scope));
}
@ -4288,6 +4420,15 @@ class htmlResponsiveRadio extends htmlRadio {
return $row->generateHTML($module, $input, $values, $restricted, $tabindex, $scope);
}
/**
* Returns the selector to use to find the show/hide elements.
*
* @return string selector
*/
protected function getShowHideSelector() {
return '.row';
}
}
/**

777
lam/lib/import.inc Normal file
View File

@ -0,0 +1,777 @@
<?php
namespace LAM\TOOLS\IMPORT_EXPORT;
use \htmlStatusMessage;
use \LAMException;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2018 Roland Gruber
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* LDIF import.
*
* @author Roland Gruber
* @package tools
*/
/** LDAP handle */
include_once('ldap.inc');
/**
* Imports LDIF files.
*
* @author Roland Gruber
* @package tools
*/
class Importer {
const SESSION_KEY_TASKS = 'import_tasks';
const SESSION_KEY_COUNT = 'import_count';
const SESSION_KEY_STOP_ON_ERROR = 'import_stop_on_error';
const STATUS = 'status';
const PROGRESS = 'progress';
const DATA = 'data';
const TIME_LIMIT = 10;
const KEY = 0;
const VALUE = 1;
const CHANGETYPE = 'changeType';
/**
* Extracts the single entries in the file.
*
* @param string[] $lines LDIF lines
* @return string|array array of string[]
* @throws LAMException invalid format
*/
private function extractImportChunks($lines) {
$entries = array();
$currentEntry = array();
foreach ($lines as $line) {
if (substr(trim($line), 0, 1) === '#') {
// skip comments
continue;
}
if (empty(trim($line))) {
// end of entry
if (!empty($currentEntry)) {
$entries[] = $currentEntry;
$currentEntry = array();
}
}
elseif (substr($line, 0, 1) === ' ') {
// append to last line if starting with a space
if (empty($currentEntry)) {
throw new LAMException(_('Invalid data'), htmlspecialchars($line));
}
else {
$currentEntry[sizeof($currentEntry) - 1] .= substr($line, 1);
}
}
elseif ($line === '-') {
$currentEntry[] = $line;
}
else {
$parts = explode(':', $line, 2);
if (sizeof($parts) < 2) {
throw new LAMException(_('Invalid data'), htmlspecialchars($line));
}
$currentEntry[] = $line;
}
}
if (!empty($currentEntry)) {
$entries[] = $currentEntry;
}
return $entries;
}
/**
* Converts the lines to LDAP import tasks.
*
* @param string[] $lines import lines
* @throws LAMException if invalid format was found
* @return ImporterTask[] tasks
*/
public function getTasks($lines) {
$chunks = $this->extractImportChunks($lines);
$tasks = $this->convertToTasks($chunks);
return $tasks;
}
/**
* Processes the import data stored in session.
*/
public function doImport() {
$data = '';
$tasks = &$_SESSION[Importer::SESSION_KEY_TASKS];
$stopOnError = $_SESSION[Importer::SESSION_KEY_STOP_ON_ERROR];
// check if any actions are needed at all
if (empty($tasks)) {
return $this->getStatus($data);
}
$endTime = $this->getEndTime();
while ((!empty($tasks)) && ($endTime > time())) {
$task = array_shift($tasks);
try {
$data .= $task->run();
}
catch (LAMException $e) {
if ($stopOnError) {
return $this->stopImport($data, $e);
}
else {
$data .= Importer::formatMessage('ERROR', $e->getTitle(), $e->getMessage());
}
}
}
return $this->getStatus($data);
}
/**
* Stops the import process because of an exception.
*
* @param string $data HTML output
* @param LAMException $e exception
* @return string JSON status
*/
private function stopImport($data, LAMException $e) {
$data .= Importer::formatMessage('ERROR', $e->getTitle(), $e->getMessage());
if (isset($_SESSION[Importer::SESSION_KEY_TASKS])) {
unset($_SESSION[Importer::SESSION_KEY_TASKS]);
}
$status = array(
Importer::STATUS => 'failed',
Importer::DATA => $data
);
return json_encode($status);
}
/**
* Returns the current status as JSON.
*
* @param string $data HTML output to display
* @return string JSON status
*/
private function getStatus($data) {
if (empty($_SESSION[Importer::SESSION_KEY_TASKS])) {
if (isset($_SESSION[Importer::SESSION_KEY_TASKS])) {
unset($_SESSION[Importer::SESSION_KEY_TASKS]);
}
$status = array(
Importer::STATUS => 'done',
Importer::DATA => $data
);
return json_encode($status);
}
$progress = (sizeof($_SESSION[Importer::SESSION_KEY_TASKS]) / $_SESSION[Importer::SESSION_KEY_COUNT]) * 100.0;
$progress = floor(100 - $progress);
$status = array(
Importer::STATUS => 'inProgress',
Importer::PROGRESS => $progress,
Importer::DATA => $data
);
return json_encode($status);
}
/**
* Returns the time when processing should end.
*
* @return number end time as Unix timestamp
*/
private function getEndTime() {
$startTime = time();
$maxTime = get_cfg_var('max_execution_time') - 10;
if ($maxTime > Importer::TIME_LIMIT) {
$maxTime = Importer::TIME_LIMIT;
}
if ($maxTime <= 0) {
$maxTime = Importer::TIME_LIMIT;
}
return $startTime + $maxTime;
}
/**
* Continues the import with processing of a single entry.
*
* @param array[] $entries import entries
* @return ImporterTask[] tasks
*/
private function convertToTasks($entries) {
$tasks = array();
$count = sizeof($entries);
for ($i = 0; $i < $count; $i++) {
$entry = $entries[$i];
$firstParts = explode(':', $entry[0], 2);
if ($firstParts[Importer::KEY] == 'version') {
if ($i > 0) {
// allow version only as first chunk
throw new LAMException(_('Invalid data'), _('Duplicate version entry found.'));
}
$this->processVersion($entry);
}
elseif ($firstParts[Importer::KEY] == 'dn') {
$tasks[] = $this->processDnEntry($entry);
}
else {
throw new LAMException(_('A valid dn line is required'), htmlspecialchars($entry[0]));
}
}
return $tasks;
}
/**
* Checks a version entry.
*
* @param string[] $entry entry
* @throws LAMException if version is invalid
*/
private function processVersion($entry) {
$keyValue = $this->getLineKeyValue($entry[0]);
if (($keyValue[Importer::VALUE] != '1') || (sizeof($entry) > 1)) {
$escapedLines = array_map('htmlspecialchars', $entry);
throw new LAMException(_('LDIF import only supports version 1.'), implode('<br>', $escapedLines));
}
}
/**
* Checks a dn entry.
*
* @param string[] $entry entry
* @return ImporterTask task
* @throws LAMException if invalid format
*/
private function processDnEntry($entry) {
$dnLine = array_shift($entry);
$keyValue = $this->getLineKeyValue($dnLine);
$dn = $keyValue[Importer::VALUE];
if (empty($entry)) {
throw new LAMException(_('Invalid data'), htmlspecialchars($dnLine));
}
$firstAttributeLine = array_shift($entry);
$firstAttribute = $this->getLineKeyValue($firstAttributeLine);
if ($firstAttribute[Importer::KEY] != Importer::CHANGETYPE) {
// complete DN
$attributes = array(
$firstAttribute[Importer::KEY] => array($firstAttribute[Importer::VALUE])
);
foreach ($entry as $attributeLine) {
$attribute = $this->getLineKeyValue($attributeLine);
$attributes[$attribute[Importer::KEY]][] = $attribute[Importer::VALUE];
}
return new AddEntryTask($dn, $attributes);
}
else {
$type = $firstAttribute[Importer::VALUE];
if ($type === 'add') {
$attributes = array();
foreach ($entry as $line) {
$lineData = $this->getLineKeyValue($line);
$attributes[$lineData[Importer::KEY]][] = $lineData[Importer::VALUE];
}
return new AddEntryTask($dn, $attributes);
}
elseif ($type === 'modrdn') {
return $this->createModRdnTask($dn, $entry);
}
elseif ($type === 'delete') {
if (!empty($entry)) {
throw new LAMException(_('Invalid data'), htmlspecialchars($dn));
}
return new DeleteEntryTask($dn);
}
elseif ($type !== 'modify') {
throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - changeType: ' . htmlspecialchars($type));
}
$changes = array();
$subtasks = array();
$currentLines = array();
$linesCount = sizeof($entry);
for ($i = 0; $i < $linesCount; $i++) {
$line = $entry[$i];
if ($line === '-') {
$subtasks[] = $this->getChangeTypeTask($dn, $currentLines);
$currentLines = array();
}
else {
$currentLines[] = $line;
}
}
$subtasks[] = $this->getChangeTypeTask($dn, $currentLines);
return new MultiTask($subtasks, $dn);
}
}
/**
* Returns a modrdn task.
*
* @param string $dn DN
* @param string[] $entry entry lines
* @return
* @throws LAMException syntax error
*/
private function createModRdnTask($dn, $entry) {
if (sizeof($entry) !== 2) {
throw new LAMException(_('Invalid data'), htmlspecialchars($dn));
}
$newRdnData = $this->getLineKeyValue($entry[0]);
if ($newRdnData[Importer::KEY] !== 'newrdn') {
throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . '<br>' . $newRdnData);
}
$newRdn = $newRdnData[Importer::VALUE];
$delOldRdnData = $this->getLineKeyValue($entry[1]);
if (($delOldRdnData[Importer::KEY] !== 'deleteoldrdn') || !in_array($delOldRdnData[Importer::VALUE], array('0', '1'), true)) {
throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . '<br>' . $entry[1]);
}
$delOldRdn = ($delOldRdnData[Importer::VALUE] === '0') ? false : true;
return new RenameEntryTask($dn, $newRdn, $delOldRdn);
}
/**
* Returns a task for LDIF changeType entry.
*
* @param string $dn DN
* @param string $lines lines
* @return ImporterTask task
*/
private function getChangeTypeTask($dn, $lines) {
$firstLine = array_shift($lines);
$firstLineData = $this->getLineKeyValue($firstLine);
$type = $firstLineData[Importer::KEY];
$attributeName = $firstLineData[Importer::VALUE];
$attributes = array();
foreach ($lines as $line) {
$lineData = $this->getLineKeyValue($line);
if ($lineData[Importer::KEY] !== $attributeName) {
throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - ' . htmlspecialchars($type));
}
$attributes[$attributeName][] = $lineData[Importer::VALUE];
}
if ($type === 'add') {
return new AddAttributesTask($dn, $attributes);
}
elseif ($type === 'delete') {
return new DeleteAttributesTask($dn, $attributeName, $attributes);
}
elseif ($type === 'replace') {
return new ReplaceAttributesTask($dn, $attributes);
}
throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - ' . htmlspecialchars($type));
}
/**
* Returns the HTML for an error message.
*
* @param string $type message type (e.g. INFO)
* @param string $title title
* @param string $message message
* @return string HTML
*/
public static function formatMessage($type, $title, $message) {
$msg = new htmlStatusMessage($type, $title, $message);
$tabindex = 0;
ob_start();
$msg->generateHTML(null, array($msg), array(), true, $tabindex, 'user');
$data = ob_get_contents();
ob_clean();
return $data;
}
/**
* Returns the key and value part of the line.
*
* @param string $line line
* @return string[] array(key, value)
*/
private function getLineKeyValue($line) {
$parts = explode(':', $line, 2);
if (sizeof($parts) !== 2) {
throw new LAMException(_('Invalid data'), htmlspecialchars($line));
}
if (substr($parts[Importer::VALUE], 0, 1) == ':') {
$value = base64_decode(trim(substr($parts[Importer::VALUE], 1)));
}
else {
$value = trim($parts[Importer::VALUE]);
}
return array($parts[Importer::KEY], $value);
}
}
/**
* A single import task.
*
* @author Roland Gruber
*/
interface ImporterTask {
/**
* Runs the task.
*
* @return string HTML output or LAMException if error occured
*/
public function run();
}
/**
* Adds a complete LDAP entry.
*
* @author Roland Gruber
*/
class AddEntryTask implements ImporterTask {
private $dn = '';
private $attributes = array();
/**
* Constructor
*
* @param string $dn DN
* @param array[string[]] $attributes list of attributes
*/
public function __construct($dn, $attributes) {
$this->dn = $dn;
$this->attributes = $attributes;
}
/**
* {@inheritDoc}
* @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
*/
public function run() {
$ldap = $_SESSION['ldap']->server();
$success = @ldap_add($ldap, $this->dn, $this->attributes);
if ($success) {
return Importer::formatMessage('INFO', _('Entry created'), htmlspecialchars($this->dn));
}
throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap));
}
}
/**
* Renames an LDAP entry.
*
* @author Roland Gruber
*/
class RenameEntryTask implements ImporterTask {
private $dn = '';
private $newRdn = '';
private $deleteOldRdn = true;
/**
* Constructor
*
* @param string $dn DN
* @param string $newRdn new RDN value
* @param bool $deleteOldRdn delete old RDN value
*/
public function __construct($dn, $newRdn, $deleteOldRdn) {
$this->dn = $dn;
$this->newRdn = $newRdn;
$this->deleteOldRdn = $deleteOldRdn;
}
/**
* {@inheritDoc}
* @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
*/
public function run() {
$ldap = $_SESSION['ldap']->server();
$success = @ldap_rename($ldap, $this->dn, $this->newRdn, null, $this->deleteOldRdn);
if ($success) {
return Importer::formatMessage('INFO', _('Rename successful!'), htmlspecialchars($this->dn));
}
throw new LAMException(_('Could not rename the entry.') . '<br>' . $this->dn, getExtendedLDAPErrorMessage($ldap));
}
}
/**
* Deletes an LDAP entry.
*
* @author Roland Gruber
*/
class DeleteEntryTask implements ImporterTask {
private $dn = '';
/**
* Constructor
*
* @param string $dn DN
*/
public function __construct($dn) {
$this->dn = $dn;
}
/**
* {@inheritDoc}
* @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
*/
public function run() {
$ldap = $_SESSION['ldap']->server();
$success = @ldap_delete($ldap, $this->dn);
if ($success) {
return Importer::formatMessage('INFO', sprintf(_('Successfully deleted DN %s'), $this->dn), '');
}
throw new LAMException(_('Could not delete the entry.') . '<br>' . $this->dn, getExtendedLDAPErrorMessage($ldap));
}
}
/**
* Combines multiple import tasks.
*
* @author Roland Gruber
*/
class MultiTask implements ImporterTask {
/**
* @var ImporterTask[] tasks
*/
private $tasks = array();
/**
* @var string DN
*/
private $dn = null;
/**
* Constructor
*
* @param ImporterTask[] $tasks tasks
*/
public function __construct($tasks, $dn) {
$this->tasks = $tasks;
$this->dn = $dn;
}
/**
* {@inheritDoc}
* @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
*/
public function run() {
foreach ($this->tasks as $task) {
$task->run();
}
return Importer::formatMessage('INFO', _('LDAP operation successful.'), htmlspecialchars($this->dn));
}
/**
* Returns the list of subtasks.
*
* @return ImporterTask[]
*/
public function getTasks() {
return $this->tasks;
}
}
/**
* Adds attributes to an existing LDAP entry.
*
* @author Roland Gruber
*/
class AddAttributesTask implements ImporterTask {
private $dn = '';
private $attributes = array();
/**
* Constructor
*
* @param string $dn DN
* @param array[string[]] $attributes list of attributes
*/
public function __construct($dn, $attributes) {
$this->dn = $dn;
$this->attributes = $attributes;
}
/**
* {@inheritDoc}
* @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
*/
public function run() {
$ldap = $_SESSION['ldap']->server();
$success = @ldap_mod_add($ldap, $this->dn, $this->attributes);
if ($success) {
return '';
}
throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap));
}
/**
* Returns the DN.
*
* @return string DN
*/
public function getDn() {
return $this->dn;
}
/**
* Returns the attributes to add.
*
* @return string[] attributes (array('attr' => array('val1', 'val2')))
*/
public function getAttributes() {
return $this->attributes;
}
}
/**
* Deletes attributes from an existing LDAP entry.
*
* @author Roland Gruber
*/
class DeleteAttributesTask implements ImporterTask {
private $dn = '';
private $attributes = array();
private $attributeName = null;
/**
* Constructor
*
* @param string $dn DN
* @param string $attributeName attribute name
* @param array[string[]] $attributes list of attributes
*/
public function __construct($dn, $attributeName, $attributes) {
$this->dn = $dn;
$this->attributeName = $attributeName;
$this->attributes = $attributes;
}
/**
* {@inheritDoc}
* @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
*/
public function run() {
$ldap = $_SESSION['ldap']->server();
if (!empty($this->attributes)) {
$success = @ldap_mod_del($ldap, $this->dn, $this->attributes);
}
else {
$success = @ldap_modify($ldap, $this->dn, array($this->attributeName => array()));
}
if ($success) {
return '';
}
throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap));
}
/**
* Returns the DN.
*
* @return string DN
*/
public function getDn() {
return $this->dn;
}
/**
* Returns the attributes to add.
*
* @return string[] attributes (array('attr' => array('val1', 'val2')))
*/
public function getAttributes() {
return $this->attributes;
}
/**
* Returns the attributes name.
*
* @return string name
*/
public function getAttributeName() {
return $this->attributeName;
}
}
/**
* Replaces attributes in an existing LDAP entry.
*
* @author Roland Gruber
*/
class ReplaceAttributesTask implements ImporterTask {
private $dn = '';
private $attributes = array();
/**
* Constructor
*
* @param string $dn DN
* @param array[string[]] $attributes list of attributes
*/
public function __construct($dn, $attributes) {
$this->dn = $dn;
$this->attributes = $attributes;
}
/**
* {@inheritDoc}
* @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
*/
public function run() {
$ldap = $_SESSION['ldap']->server();
$success = @ldap_modify($ldap, $this->dn, $this->attributes);
if ($success) {
return '';
}
throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap));
}
/**
* Returns the DN.
*
* @return string DN
*/
public function getDn() {
return $this->dn;
}
/**
* Returns the attributes to add.
*
* @return string[] attributes (array('attr' => array('val1', 'val2')))
*/
public function getAttributes() {
return $this->attributes;
}
/**
* Returns the attributes name.
*
* @return string name
*/
public function getAttributeName() {
return $this->attributeName;
}
}
?>

View File

@ -0,0 +1,131 @@
<?php
namespace LAM\TOOLS\UPLOAD;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2018 Roland Gruber
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* Import and export tool.
*
* @author Roland Gruber
* @package tools
*/
/**
* Import and export tool.
*
* @package tools
*/
class ImportExport implements \LAMTool {
/**
* Returns the name of the tool.
*
* @return string name
*/
function getName() {
return _("LDAP import/export");
}
/**
* returns a description text for the tool.
*
* @return string description
*/
function getDescription() {
return _("Imports and exports LDAP data.");
}
/**
* Returns a link to the tool page (relative to templates/).
*
* @return string link
*/
function getLink() {
return "tools/importexport.php";
}
/**
* Returns if the tool requires write access to LDAP.
*
* @return boolean true if write access is needed
*/
function getRequiresWriteAccess() {
return true;
}
/**
* Returns if the tool requires password change rights.
*
* @return boolean true if password change rights are needed
*/
function getRequiresPasswordChangeRights() {
return true;
}
/**
* Returns the link to the tool image (relative to graphics/)
*
* @return string image URL
*/
function getImageLink() {
return 'importexport.png';
}
/**
* Returns the prefered position of this tool on the tools page.
* The position may be between 0 and 1000. 0 is the top position.
*
* @return int prefered position
*/
function getPosition() {
return 450;
}
/**
* Returns a list of sub tools or an empty array.
*
* @return array list of subtools (LAMTool)
*/
function getSubTools() {
return array();
}
/**
* Returns if the tool is visible in the menu.
*
* @return boolean visible
*/
function isVisible() {
return true;
}
/**
* Returns if a tool may be hidden by configuration in the LAM server profile.
*
* @return boolean hideable
*/
function isHideable() {
return true;
}
}
?>

View File

@ -380,6 +380,12 @@ table.collapse {
background-position: 0px 0px !important;
}
.okButton {
background-image: url(../graphics/pass.png) !important;
background-size: 16px 16px;
background-position: 0px 0px !important;
}
.smallPadding span {
padding: 0.1em 0.4em !important;
}

View File

@ -39,10 +39,10 @@ $config->custom->commands['script'] = array(
'delete_form' => true,
'draw_tree_node' => true,
'expand' => true,
'export' => true,
'export_form' => true,
'import' => true,
'import_form' => true,
'export' => false,
'export_form' => false,
'import' => false,
'import_form' => false,
'login' => true,
'logout' => true,
'login_form' => true,

View File

@ -901,6 +901,210 @@ window.lam.tools.schema.select = function() {
});
};
window.lam.importexport = window.lam.importexport || {};
/**
* Starts the import process.
*
* @param tokenName name of CSRF token
* @param tokenValue value of CSRF token
*/
window.lam.importexport.startImport = function(tokenName, tokenValue) {
jQuery(document).ready(function() {
jQuery('#progressbarImport').progressbar();
var output = jQuery('#importResults');
var data = {
jsonInput: ''
};
data[tokenName] = tokenValue;
jQuery.ajax({
url: '../misc/ajax.php?function=import',
method: 'POST',
data: data
})
.done(function(jsonData){
if (jsonData.data && (jsonData.data != '')) {
output.append(jsonData.data);
}
if (jsonData.status == 'done') {
jQuery('#progressbarImport').hide();
jQuery('#btn_submitImportCancel').hide();
jQuery('#statusImportInprogress').hide();
jQuery('#statusImportDone').show();
jQuery('.newimport').show();
}
else if (jsonData.status == 'failed') {
jQuery('#btn_submitImportCancel').hide();
jQuery('#statusImportInprogress').hide();
jQuery('#statusImportFailed').show();
jQuery('.newimport').show();
}
else {
jQuery('#progressbarImport').progressbar({
value: jsonData.progress
});
window.lam.import.startImport(tokenName, tokenValue);
}
});
});
};
/**
* Starts the export process.
*
* @param tokenName name of CSRF token
* @param tokenValue value of CSRF token
*/
window.lam.importexport.startExport = function(tokenName, tokenValue) {
jQuery(document).ready(function() {
jQuery('#progressbarExport').progressbar({value: 50});
var output = jQuery('#exportResults');
var data = {
jsonInput: ''
};
data[tokenName] = tokenValue;
data['baseDn'] = jQuery('#baseDn').val();
data['searchScope'] = jQuery('#searchScope').val();
data['filter'] = jQuery('#filter').val();
data['attributes'] = jQuery('#attributes').val();
data['format'] = jQuery('#format').val();
data['ending'] = jQuery('#ending').val();
data['includeSystem'] = jQuery('#includeSystem').val();
data['saveAsFile'] = jQuery('#saveAsFile').val();
jQuery.ajax({
url: '../misc/ajax.php?function=export',
method: 'POST',
data: data
})
.done(function(jsonData){
if (jsonData.data && (jsonData.data != '')) {
output.append(jsonData.data);
}
if (jsonData.status == 'done') {
jQuery('#progressbarExport').hide();
jQuery('#btn_submitExportCancel').hide();
jQuery('#statusExportInprogress').hide();
jQuery('#statusExportDone').show();
jQuery('.newexport').show();
if (jsonData.output) {
jQuery('#exportResults > pre').text(jsonData.output);
}
else if (jsonData.file) {
window.open(jsonData.file, '_blank');
}
}
else {
jQuery('#progressbarExport').hide();
jQuery('#btn_submitExportCancel').hide();
jQuery('#statusExportInprogress').hide();
jQuery('#statusExportFailed').show();
jQuery('.newexport').show();
}
})
.fail(function() {
jQuery('#progressbarExport').hide();
jQuery('#btn_submitExportCancel').hide();
jQuery('#statusExportInprogress').hide();
jQuery('#statusExportFailed').show();
jQuery('.newexport').show();
});
});
};
window.lam.html = window.lam.html || {};
/**
* Shows a DN selection for the given input field.
*
* @param fieldId id of input field
* @param title title of dialog
* @param okText ok button text
* @param cancelText cancel button text
* @param tokenName CSRF token name
* @param tokenValue CSRF token value
*/
window.lam.html.showDnSelection = function(fieldId, title, okText, cancelText, tokenName, tokenValue) {
var field = jQuery('#' + fieldId);
var fieldDiv = jQuery('#dlg_' + fieldId);
if (!fieldDiv.length > 0) {
jQuery('body').append(jQuery('<div class="hidden" id="dlg_' + fieldId + '"></div>'));
}
var dnValue = field.val();
var data = {
jsonInput: ''
};
data[tokenName] = tokenValue;
data['fieldId'] = fieldId;
data['dn'] = dnValue;
jQuery.ajax({
url: '../misc/ajax.php?function=dnselection',
method: 'POST',
data: data
})
.done(function(jsonData) {
jQuery('#dlg_' + fieldId).html(jsonData.dialogData);
var buttonList = {};
buttonList[cancelText] = function() { jQuery(this).dialog("destroy"); };
jQuery('#dlg_' + fieldId).dialog({
modal: true,
title: title,
dialogClass: 'defaultBackground',
buttons: buttonList,
width: 'auto',
maxHeight: 600,
position: {my: 'center', at: 'center', of: window}
});
});
};
/**
* Selects the DN from dialog.
*
* @param el ok button in dialog
* @param fieldId field id of input field
* @returns false
*/
window.lam.html.selectDn = function(el, fieldId) {
var field = jQuery('#' + fieldId);
var dn = jQuery(el).parents('.row').data('dn');
field.val(dn);
jQuery('#dlg_' + fieldId).dialog("destroy");
return false;
}
/**
* Updates the DN selection.
*
* @param el element
* @param fieldId field id of dialog
* @param tokenName CSRF token name
* @param tokenValue CSRF token value
*/
window.lam.html.updateDnSelection = function(el, fieldId, tokenName, tokenValue) {
var fieldDiv = jQuery('#dlg_' + fieldId);
var dn = jQuery(el).parents('.row').data('dn');
var data = {
jsonInput: ''
};
data[tokenName] = tokenValue;
data['fieldId'] = fieldId;
data['dn'] = dn;
jQuery.ajax({
url: '../misc/ajax.php?function=dnselection',
method: 'POST',
data: data
})
.done(function(jsonData) {
jQuery('#dlg_' + fieldId).html(jsonData.dialogData);
jQuery(fieldDiv).dialog({
position: {my: 'center', at: 'center', of: window}
});
})
.fail(function() {
jQuery(fieldDiv).dialog("close");
});
}
jQuery(document).ready(function() {
window.lam.gui.equalHeight();
window.lam.form.autoTrim();

View File

@ -1,5 +1,12 @@
<?php
namespace LAM\AJAX;
use \LAM\TOOLS\IMPORT_EXPORT\Importer;
use \LAM\TOOLS\IMPORT_EXPORT\Exporter;
use \LAM\TYPES\TypeManager;
use \htmlResponsiveRow;
use \htmlLink;
use \htmlOutputText;
use \htmlButton;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
@ -30,6 +37,8 @@ namespace LAM\AJAX;
/** security functions */
include_once("../../lib/security.inc");
/** LDIF import */
include_once("../../lib/import.inc");
// start session
if (isset($_GET['selfservice'])) {
@ -91,7 +100,31 @@ class Ajax {
if ($function == 'passwordChange') {
$this->managePasswordChange($jsonInput);
}
elseif ($function == 'upload') {
elseif ($function === 'import') {
include_once('../../lib/import.inc');
$importer = new Importer();
ob_start();
$jsonOut = $importer->doImport();
ob_end_clean();
echo $jsonOut;
}
elseif ($function === 'export') {
include_once('../../lib/export.inc');
$attributes = $_POST['attributes'];
$baseDn = $_POST['baseDn'];
$ending = $_POST['ending'];
$filter = $_POST['filter'];
$format = $_POST['format'];
$includeSystem = ($_POST['includeSystem'] === 'true');
$saveAsFile = ($_POST['saveAsFile'] === 'true');
$searchScope = $_POST['searchScope'];
$exporter = new Exporter($baseDn, $searchScope, $filter, $attributes, $includeSystem, $saveAsFile, $format, $ending);
ob_start();
$jsonOut = $exporter->doExport();
ob_end_clean();
echo $jsonOut;
}
elseif ($function === 'upload') {
include_once('../../lib/upload.inc');
$typeManager = new \LAM\TYPES\TypeManager();
$uploader = new \LAM\UPLOAD\Uploader($typeManager->getConfiguredType($_GET['typeId']));
@ -100,6 +133,12 @@ class Ajax {
ob_end_clean();
echo $jsonOut;
}
elseif ($function === 'dnselection') {
ob_start();
$jsonOut = $this->dnSelection();
ob_end_clean();
echo $jsonOut;
}
}
/**
@ -132,6 +171,128 @@ class Ajax {
echo json_encode(array("result" => $result));
}
/**
* Handles DN selection fields.
*
* @return string JSON output
*/
private function dnSelection() {
$dn = trim($_POST['dn']);
if (empty($dn) || !get_preg($dn, 'dn')) {
$dnList = $this->getDefaultDns();
$dn = null;
}
else {
$dnList = $this->getSubDns($dn);
}
$html = $this->buildDnSelectionHtml($dnList, $dn);
return json_encode(array('dialogData' => $html));
}
/**
* Returns a list of default DNs from account types + tree suffix.
*
* @return string[] default DNs
*/
private function getDefaultDns() {
$typeManager = new TypeManager();
$baseDnList = array();
foreach ($typeManager->getConfiguredTypes() as $type) {
$suffix = $type->getSuffix();
if (!empty($suffix)) {
$baseDnList[] = $suffix;
}
}
$treeSuffix = $_SESSION['config']->get_Suffix('tree');
if (!empty($treeSuffix)) {
$baseDnList[] = $suffix;
}
$baseDnList = array_unique($baseDnList);
usort($baseDnList, 'compareDN');
return $baseDnList;
}
/**
* Returns the HTML to build the DN selection list.
*
* @param string[] $dnList DN list
* @param string $currentDn current DN
*/
private function buildDnSelectionHtml($dnList, $currentDn) {
$fieldId = trim($_POST['fieldId']);
$mainRow = new htmlResponsiveRow();
$onclickUp = 'window.lam.html.updateDnSelection(this, \''
. htmlspecialchars($fieldId) . '\', \'' . getSecurityTokenName() . '\', \''
. getSecurityTokenValue() . '\')';
if (!empty($currentDn)) {
$row = new htmlResponsiveRow();
$row->addDataAttribute('dn', $currentDn);
$text = new htmlOutputText($currentDn);
$text->setIsBold(true);
$row->add($text, 12, 9);
$row->setCSSClasses(array('text-right'));
$buttonId = base64_encode($currentDn);
$buttonId = str_replace('=', '', $buttonId);
$button = new htmlButton($buttonId, _('Ok'));
$button->setIconClass('okButton');
$button->setOnClick('window.lam.html.selectDn(this, \'' . htmlspecialchars($fieldId) . '\')');
$row->add($button, 12, 3);
$mainRow->add($row, 12);
// back up
$row = new htmlResponsiveRow();
$row->addDataAttribute('dn', extractDNSuffix($currentDn));
$text = new htmlLink('..', '#');
$text->setCSSClasses(array('bold'));
$text->setOnClick($onclickUp);
$row->add($text, 12, 9);
$row->setCSSClasses(array('text-right'));
$buttonId = base64_encode('..');
$buttonId = str_replace('=', '', $buttonId);
$button = new htmlButton($buttonId, _('Up'));
$button->setIconClass('upButton');
$button->setOnClick($onclickUp);
$row->add($button, 12, 3);
$mainRow->add($row, 12);
}
foreach ($dnList as $dn) {
$row = new htmlResponsiveRow();
$row->addDataAttribute('dn', $dn);
$link = new htmlLink($dn, '#');
$link->setOnClick($onclickUp);
$row->add($link, 12, 9);
$row->setCSSClasses(array('text-right'));
$buttonId = base64_encode($dn);
$buttonId = str_replace('=', '', $buttonId);
$button = new htmlButton($buttonId, _('Ok'));
$button->setIconClass('okButton');
$button->setOnClick('window.lam.html.selectDn(this, \'' . htmlspecialchars($fieldId) . '\')');
$row->add($button, 12, 3);
$mainRow->add($row, 12);
}
$tabindex = 1000;
ob_start();
parseHtml(null, $mainRow, array(), false, $tabindex, 'user');
$out = ob_get_contents();
ob_end_clean();
return $out;
}
/**
* Returns the sub DNs of given DN.
*
* @param string $dn DN
* @return string[] sub DNs
*/
private function getSubDns($dn) {
$dnEntries = ldapListDN($dn);
$dnList = array();
foreach ($dnEntries as $entry) {
$dnList[] = $entry['dn'];
}
usort($dnList, 'compareDN');
return $dnList;
}
}

View File

@ -0,0 +1,397 @@
<?php
namespace LAM\TOOLS\IMPORT_EXPORT;
use \htmlTitle;
use \htmlResponsiveRadio;
use \htmlResponsiveRow;
use \htmlResponsiveInputFileUpload;
use \htmlResponsiveInputTextarea;
use \htmlButton;
use \htmlStatusMessage;
use \htmlDiv;
use \htmlOutputText;
use \htmlJavaScript;
use \LAMException;
use \htmlLink;
use \htmlResponsiveInputCheckbox;
use \htmlResponsiveSelect;
use \htmlResponsiveInputField;
use \htmlHiddenInput;
use LAM\TYPES\TypeManager;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2018 Roland Gruber
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* Multi edit tool that allows LDAP operations on multiple entries.
*
* @author Roland Gruber
* @package tools
*/
/** security functions */
include_once("../../lib/security.inc");
/** access to configuration data */
include_once("../../lib/config.inc");
/** access LDAP server */
include_once("../../lib/ldap.inc");
/** used to print status messages */
include_once("../../lib/status.inc");
/** import class */
include_once("../../lib/import.inc");
// start session
startSecureSession();
enforceUserIsLoggedIn();
// die if no write access
if (!checkIfWriteAccessIsAllowed()) die();
checkIfToolIsActive('ImportExport');
setlanguage();
if (!empty($_POST)) {
validateSecurityToken();
}
// clean old data
if (isset($_SESSION[Importer::SESSION_KEY_TASKS])) {
unset($_SESSION[Importer::SESSION_KEY_TASKS]);
}
if (isset($_SESSION[Importer::SESSION_KEY_COUNT])) {
unset($_SESSION[Importer::SESSION_KEY_COUNT]);
}
if (isset($_SESSION[Importer::SESSION_KEY_STOP_ON_ERROR])) {
unset($_SESSION[Importer::SESSION_KEY_STOP_ON_ERROR]);
}
include '../../lib/adminHeader.inc';
$tabindex = 1;
$activeTab = 0;
if (!empty($_GET['tab']) && ($_GET['tab'] === 'export')) {
$activeTab = 1;
}
?>
<script>
$(function() {
jQuery("#tabs").tabs({
active: <?php echo $activeTab; ?>
});
jQuery(".inputForm").validationEngine();
});
</script>
<div class="user-bright smallPaddingContent">
<div id="tabs">
<ul>
<li id="tab_import">
<a href="#tab-import"><img alt="import" src="../../graphics/import.png"> <?php echo _('Import') ?> </a>
</li>
<li id="tab_export">
<a href="#tab-export"><img alt="export" src="../../graphics/export.png"> <?php echo _('Export') ?> </a>
</li>
</ul>
<div id="tab-import">
<?php
if (isset($_POST['submitImport'])) {
printImportTabProcessing($tabindex);
}
else {
printImportTabContent($tabindex);
}
?>
</div>
<div id="tab-export">
<?php
if (isset($_POST['submitExport'])) {
printExportTabProcessing($tabindex);
}
else {
printExportTabContent($tabindex);
}
?>
</div>
</div>
</div>
<?php
/**
* Prints the content area for the import tab.
*
* @param int $tabindex tabindex
*/
function printImportTabContent(&$tabindex) {
echo "<form class=\"inputForm\" enctype=\"multipart/form-data\" action=\"importexport.php\" method=\"post\">\n";
$container = new htmlResponsiveRow();
$container->add(new htmlTitle(_("Import")), 12);
$sources = array(
_('Text input') => 'text',
_('File') => 'file',
);
$sourceRadio = new htmlResponsiveRadio(_('Source'), 'source', $sources, 'text');
$sourceRadio->setTableRowsToHide(
array(
'file' => array('text'),
'text' => array('file')
)
);
$sourceRadio->setTableRowsToShow(
array(
'text' => array('text'),
'file' => array('file')
)
);
$container->add($sourceRadio, 12);
$container->addVerticalSpacer('1rem');
$container->add(new htmlResponsiveInputFileUpload('file', _('File'), '750'), 12);
$container->add(new htmlResponsiveInputTextarea('text', '', '60', '20', _('LDIF data'), '750'), 12);
$container->add(new htmlResponsiveInputCheckbox('noStop', false, _('Don\'t stop on errors')), 12);
$container->addVerticalSpacer('3rem');
$button = new htmlButton('submitImport', _('Submit'));
$container->add($button, 12, 12, 12, 'text-center');
addSecurityTokenToMetaHTML($container);
parseHtml(null, $container, array(), false, $tabindex, 'user');
echo ("</form>\n");
}
/**
* Prints the content area for the import tab during processing state.
*
* @param int $tabindex tabindex
*/
function printImportTabProcessing(&$tabindex) {
try {
checkImportData();
}
catch (LAMException $e) {
$container = new htmlResponsiveRow();
$container->add(new htmlStatusMessage('ERROR', $e->getTitle(), $e->getMessage()), 12);
parseHtml(null, $container, array(), false, $tabindex, 'user');
printImportTabContent($tabindex);
return;
}
echo "<form class=\"inputForm\" enctype=\"multipart/form-data\" action=\"importexport.php\" method=\"post\">\n";
$container = new htmlResponsiveRow();
$container->add(new htmlTitle(_("Import")), 12);
$container->add(new htmlDiv('statusImportInprogress', new htmlOutputText(_('Status') . ': ' . _('in progress'))), 12);
$container->add(new htmlDiv('statusImportDone', new htmlOutputText(_('Status') . ': ' . _('done')), array('hidden')), 12);
$container->add(new htmlDiv('statusImportFailed', new htmlOutputText(_('Status') . ': ' . _('failed')), array('hidden')), 12);
$container->addVerticalSpacer('1rem');
$container->add(new htmlDiv('progressbarImport', new htmlOutputText('')), 12);
$container->addVerticalSpacer('3rem');
$button = new htmlButton('submitImportCancel', _('Cancel'));
$container->add($button, 12, 12, 12, 'text-center');
$newImportButton = new htmlLink(_('New import'), null, null, true);
$container->add($newImportButton, 12, 12, 12, 'text-center hidden newimport');
$container->addVerticalSpacer('3rem');
$container->add(new htmlDiv('importResults', new htmlOutputText('')), 12);
$container->add(new htmlJavaScript(
'window.lam.importexport.startImport(\'' . getSecurityTokenName() . '\', \'' . getSecurityTokenValue() . '\');'
), 12);
addSecurityTokenToMetaHTML($container);
parseHtml(null, $container, array(), false, $tabindex, 'user');
echo ("</form>\n");
}
/**
* Checks if the import data is ok.
*
* @throws LAMException error message if not valid
*/
function checkImportData() {
$source = $_POST['source'];
$ldif = '';
if ($source == 'text') {
$ldif = $_POST['text'];
}
else {
$handle = fopen($_FILES['file']['tmp_name'], "r");
$ldif = fread($handle, 100000000);
fclose($handle);
}
if (empty($ldif)) {
throw new LAMException(_('You must either upload a file or provide an import in the text box.'));
}
$lines = preg_split("/\n|\r\n|\r/", $ldif);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$_SESSION[Importer::SESSION_KEY_TASKS] = $tasks;
$_SESSION[Importer::SESSION_KEY_COUNT] = sizeof($tasks);
$_SESSION[Importer::SESSION_KEY_STOP_ON_ERROR] = (!isset($_POST['noStop']) || ($_POST['noStop'] != 'on'));
}
/**
* Prints the content area for the export tab.
*
* @param int $tabindex tabindex
*/
function printExportTabContent(&$tabindex) {
echo "<form class=\"inputForm\" enctype=\"multipart/form-data\" action=\"importexport.php?tab=export\" method=\"post\">\n";
$container = new htmlResponsiveRow();
$container->add(new htmlTitle(_("Export")), 12);
$baseDnField = new htmlResponsiveInputField(_('Base DN'), 'baseDn', getDefaultBaseDn(), '751', true);
$baseDnField->showDnSelection();
$container->add($baseDnField, 12);
$searchScopes = array(
_('Base (base dn only)') => 'base',
_('One (one level beneath base)') => 'one',
_('Sub (entire subtree)') => 'sub'
);
$searchScopeSelect = new htmlResponsiveSelect('searchScope', $searchScopes, array('sub'), _('Search scope'));
$searchScopeSelect->setHasDescriptiveElements(true);
$searchScopeSelect->setSortElements(false);
$container->add($searchScopeSelect, 12);
$container->add(new htmlResponsiveInputField(_('Search filter'), 'filter', '(objectClass=*)', '752'), 12);
$container->add(new htmlResponsiveInputField(_('Attributes'), 'attributes', '*', '753'), 12);
$container->add(new htmlResponsiveInputCheckbox('includeSystem', false, _('Include system attributes'), '754'), 12);
$container->add(new htmlResponsiveInputCheckbox('saveAsFile', false, _('Save as file')), 12);
$formats = array(
'CSV' => 'csv',
'LDIF' => 'ldif'
);
$formatSelect = new htmlResponsiveSelect('format', $formats, array('ldif'), _('Export format'));
$formatSelect->setHasDescriptiveElements(true);
$formatSelect->setSortElements(false);
$container->add($formatSelect, 12);
$endings = array(
'Windows' => 'windows',
'Unix' => 'unix'
);
$endingsSelect = new htmlResponsiveSelect('ending', $endings, array('unix'), _('End of line'));
$endingsSelect->setHasDescriptiveElements(true);
$endingsSelect->setSortElements(false);
$container->add($endingsSelect, 12);
$container->addVerticalSpacer('3rem');
$button = new htmlButton('submitExport', _('Submit'));
$container->add($button, 12, 12, 12, 'text-center');
addSecurityTokenToMetaHTML($container);
parseHtml(null, $container, array(), false, $tabindex, 'user');
echo ("</form>\n");
}
/**
* Returns the default base DN.
*
* @return string base DN
*/
function getDefaultBaseDn() {
$typeManager = new TypeManager();
$baseDn = '';
foreach ($typeManager->getConfiguredTypes() as $type) {
$suffix = $type->getSuffix();
if (empty($baseDn) || (!empty($suffix) && (strlen($suffix) < strlen($baseDn)))) {
$baseDn = $suffix;
}
}
$treeSuffix = $_SESSION['config']->get_Suffix('tree');
if (empty($baseDn) || (!empty($treeSuffix) && (strlen($treeSuffix) < strlen($baseDn)))) {
$baseDn = $treeSuffix;
}
return $baseDn;
}
/**
* Prints the content area for the export tab during processing state.
*
* @param int $tabindex tabindex
*/
function printExportTabProcessing(&$tabindex) {
try {
checkExportData();
}
catch (LAMException $e) {
$container = new htmlResponsiveRow();
$container->add(new htmlStatusMessage('ERROR', $e->getTitle(), $e->getMessage()), 12);
parseHtml(null, $container, array(), false, $tabindex, 'user');
printExportTabContent($tabindex);
return;
}
echo "<form class=\"inputForm\" enctype=\"multipart/form-data\" action=\"importexport.php?tab=export\" method=\"post\">\n";
$container = new htmlResponsiveRow();
$container->add(new htmlTitle(_("Export")), 12);
$container->add(new htmlHiddenInput('baseDn', $_POST['baseDn']), 12);
$container->add(new htmlHiddenInput('searchScope', $_POST['searchScope']), 12);
$container->add(new htmlHiddenInput('filter', $_POST['filter']), 12);
$container->add(new htmlHiddenInput('attributes', $_POST['attributes']), 12);
$container->add(new htmlHiddenInput('format', $_POST['format']), 12);
$container->add(new htmlHiddenInput('ending', $_POST['ending']), 12);
$container->add(new htmlHiddenInput('includeSystem', isset($_POST['includeSystem']) && ($_POST['includeSystem'] === 'on') ? 'true' : 'false'), 12);
$container->add(new htmlHiddenInput('saveAsFile', isset($_POST['saveAsFile']) && ($_POST['saveAsFile'] === 'on') ? 'true' : 'false'), 12);
$container->add(new htmlDiv('statusExportInprogress', new htmlOutputText(_('Status') . ': ' . _('in progress'))), 12);
$container->add(new htmlDiv('statusExportDone', new htmlOutputText(_('Status') . ': ' . _('done')), array('hidden')), 12);
$container->add(new htmlDiv('statusExportFailed', new htmlOutputText(_('Status') . ': ' . _('failed')), array('hidden')), 12);
$container->addVerticalSpacer('1rem');
$container->add(new htmlDiv('progressbarExport', new htmlOutputText('')), 12);
$container->addVerticalSpacer('3rem');
$button = new htmlButton('submitExportCancel', _('Cancel'));
$container->add($button, 12, 12, 12, 'text-center');
$newExportButton = new htmlLink(_('New export'), null, null, true);
$container->add($newExportButton, 12, 12, 12, 'text-center hidden newexport');
$container->addVerticalSpacer('3rem');
$exportText = new htmlOutputText('');
$exportText->setPreformatted(true);
$container->add(new htmlDiv('exportResults', $exportText), 12);
$container->add(new htmlJavaScript(
'window.lam.importexport.startExport(\'' . getSecurityTokenName() . '\', \'' . getSecurityTokenValue() . '\');'
), 12);
addSecurityTokenToMetaHTML($container);
parseHtml(null, $container, array(), false, $tabindex, 'user');
echo ("</form>\n");
}
/**
* Checks if the export data is ok.
*
* @throws LAMException error message if not valid
*/
function checkExportData() {
if (empty($_POST['baseDn'])) {
throw new LAMException(_('This field is required.'), _('Base DN'));
}
}
include '../../lib/adminFooter.inc';

View File

@ -0,0 +1,462 @@
<?php
use \LAM\TOOLS\IMPORT_EXPORT\Importer;
use LAM\TOOLS\IMPORT_EXPORT\MultiTask;
use LAM\TOOLS\IMPORT_EXPORT\AddAttributesTask;
use LAM\TOOLS\IMPORT_EXPORT\AddEntryTask;
use LAM\TOOLS\IMPORT_EXPORT\RenameEntryTask;
use LAM\TOOLS\IMPORT_EXPORT\DeleteEntryTask;
use LAM\TOOLS\IMPORT_EXPORT\DeleteAttributesTask;
use LAM\TOOLS\IMPORT_EXPORT\ReplaceAttributesTask;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2018 Roland Gruber
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
require_once 'lam/lib/import.inc';
/**
* Checks the LDIF importer.
*
* @author Roland Gruber
*/
class ImporterTest extends PHPUnit_Framework_TestCase {
/**
* No LDIF at all.
*/
public function testCompletelyInvalid() {
$lines = array(
"this is no LDIF"
);
$this->setExpectedException(LAMException::class, 'this is no LDIF');
$importer = new Importer();
$importer->getTasks($lines);
}
/**
* Wrong format version.
*/
public function testWrongVersion() {
$lines = array(
"version: 3"
);
$this->setExpectedException(LAMException::class, 'version: 3');
$importer = new Importer();
$importer->getTasks($lines);
}
/**
* Multiple versions.
*/
public function testMultipleVersions() {
$lines = array(
"version: 1",
"",
"version: 1"
);
$this->setExpectedException(LAMException::class);
$importer = new Importer();
$importer->getTasks($lines);
}
/**
* Data after version.
*/
public function testDataAfterVersion() {
$lines = array(
"version: 1",
"some: data"
);
$this->setExpectedException(LAMException::class);
$importer = new Importer();
$importer->getTasks($lines);
}
/**
* DN line without any data.
*/
public function testDnNoData() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com"
);
$this->setExpectedException(LAMException::class, 'dn: uid=test,dc=example,dc=com');
$importer = new Importer();
$importer->getTasks($lines);
}
/**
* One complete entry.
*/
public function testSingleFullEntry() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"objectClass: inetOrgPerson",
"uid: test",
);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$this->assertEquals(1, sizeof($tasks));
}
/**
* Change entry with invalid changetype.
*/
public function testChangeInvalidType() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: invalid",
"uid: test",
);
$this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com - changeType: invalid');
$importer = new Importer();
$tasks = $importer->getTasks($lines);
}
/**
* Change entry with add changetype.
*/
public function testChangeAdd() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: add",
"uid: test",
);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$this->assertEquals(1, sizeof($tasks));
$task = $tasks[0];
$this->assertEquals(AddEntryTask::class, get_class($task));
}
/**
* Change entry with modrdn changetype and invalid options.
*/
public function testChangeModRdnInvalidData() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modrdn",
"uid: test",
);
$this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
$importer = new Importer();
$tasks = $importer->getTasks($lines);
}
/**
* Change entry with modrdn changetype and invalid deleteoldrdn.
*/
public function testChangeModRdnInvalidDeleteoldrdn() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modrdn",
"newrdn: uid1=test",
"deleteoldrdn: x",
);
$this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
$importer = new Importer();
$tasks = $importer->getTasks($lines);
}
/**
* Change entry with modrdn changetype.
*/
public function testChangeModRdn() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modrdn",
"newrdn: uid1=test",
"deleteoldrdn: 0",
);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$this->assertEquals(1, sizeof($tasks));
$task = $tasks[0];
$this->assertEquals(RenameEntryTask::class, get_class($task));
}
/**
* Change entry with delete changetype with extra line.
*/
public function testChangeDeleteInvalid() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: delete",
"uid: test",
);
$this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
$importer = new Importer();
$tasks = $importer->getTasks($lines);
}
/**
* Change entry with delete changetype.
*/
public function testChangeDelete() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: delete",
);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$this->assertEquals(1, sizeof($tasks));
$task = $tasks[0];
$this->assertEquals(DeleteEntryTask::class, get_class($task));
}
/**
* Change entry with modify changetype with invalid operation.
*/
public function testChangeModifyInvalid() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modify",
"invalid: test",
);
$this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
$importer = new Importer();
$tasks = $importer->getTasks($lines);
}
/**
* Change entry with modify changetype and add operation.
*/
public function testChangeModifyAddInvalid() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modify",
"add: uid",
"uid: uid1",
"invalid: uid2"
);
$this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
$importer = new Importer();
$tasks = $importer->getTasks($lines);
}
/**
* Change entry with modify changetype and add operation.
*/
public function testChangeModifyAdd() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modify",
"add: uid",
"uid: uid1",
"uid: uid2"
);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$this->assertEquals(1, sizeof($tasks));
$task = $tasks[0];
$this->assertEquals(MultiTask::class, get_class($task));
$subtasks = $task->getTasks();
$this->assertEquals(1, sizeof($subtasks));
$subTask = $subtasks[0];
$this->assertEquals(AddAttributesTask::class, get_class($subTask));
$this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
$attributes = $subTask->getAttributes();
$this->assertEquals(1, sizeof($attributes));
$this->assertEquals(2, sizeof($attributes['uid']));
$this->assertTrue(in_array('uid1', $attributes['uid']));
$this->assertTrue(in_array('uid2', $attributes['uid']));
}
/**
* Change entry with modify changetype and two add operations.
*/
public function testChangeModifyAddTwice() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modify",
"add: uid",
"uid: uid1",
"uid: uid2",
"-",
"add: gn",
"gn: name1",
"gn: name2"
);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$this->assertEquals(1, sizeof($tasks));
$task = $tasks[0];
$this->assertEquals(MultiTask::class, get_class($task));
$subtasks = $task->getTasks();
$this->assertEquals(2, sizeof($subtasks));
$subTask = $subtasks[0];
$this->assertEquals(AddAttributesTask::class, get_class($subTask));
$this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
$attributes = $subTask->getAttributes();
$this->assertEquals(1, sizeof($attributes));
$this->assertEquals(2, sizeof($attributes['uid']));
$this->assertTrue(in_array('uid1', $attributes['uid']));
$this->assertTrue(in_array('uid2', $attributes['uid']));
$subTask = $subtasks[1];
$this->assertEquals(AddAttributesTask::class, get_class($subTask));
$this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
$attributes = $subTask->getAttributes();
$this->assertEquals(1, sizeof($attributes));
$this->assertEquals(2, sizeof($attributes['gn']));
$this->assertTrue(in_array('name1', $attributes['gn']));
$this->assertTrue(in_array('name2', $attributes['gn']));
}
/**
* Change entry with modify changetype and delete operation.
*/
public function testChangeModifyDelete() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modify",
"delete: uid",
"uid: uid1",
"uid: uid2"
);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$this->assertEquals(1, sizeof($tasks));
$task = $tasks[0];
$this->assertEquals(MultiTask::class, get_class($task));
$subtasks = $task->getTasks();
$this->assertEquals(1, sizeof($subtasks));
$subTask = $subtasks[0];
$this->assertEquals(DeleteAttributesTask::class, get_class($subTask));
$this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
$attributes = $subTask->getAttributes();
$this->assertEquals(1, sizeof($attributes));
$this->assertEquals(2, sizeof($attributes['uid']));
$this->assertTrue(in_array('uid1', $attributes['uid']));
$this->assertTrue(in_array('uid2', $attributes['uid']));
}
/**
* Change entry with modify changetype and delete operation.
*/
public function testChangeModifyDeleteAll() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modify",
"delete: uid",
);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$this->assertEquals(1, sizeof($tasks));
$task = $tasks[0];
$this->assertEquals(MultiTask::class, get_class($task));
$subtasks = $task->getTasks();
$this->assertEquals(1, sizeof($subtasks));
$subTask = $subtasks[0];
$this->assertEquals(DeleteAttributesTask::class, get_class($subTask));
$this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
$attributes = $subTask->getAttributes();
$this->assertTrue(empty($attributes));
}
/**
* Change entry with modify changetype and replace operation.
*/
public function testChangeModifyReplace() {
$lines = array(
"version: 1",
"",
"dn: uid=test,dc=example,dc=com",
"changeType: modify",
"replace: uid",
"uid: uid1",
"uid: uid2",
);
$importer = new Importer();
$tasks = $importer->getTasks($lines);
$this->assertEquals(1, sizeof($tasks));
$task = $tasks[0];
$this->assertEquals(MultiTask::class, get_class($task));
$subtasks = $task->getTasks();
$this->assertEquals(1, sizeof($subtasks));
$subTask = $subtasks[0];
$this->assertEquals(ReplaceAttributesTask::class, get_class($subTask));
$this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
$attributes = $subTask->getAttributes();
$this->assertEquals(1, sizeof($attributes));
$this->assertEquals(2, sizeof($attributes['uid']));
$this->assertTrue(in_array('uid1', $attributes['uid']));
$this->assertTrue(in_array('uid2', $attributes['uid']));
}
}