Start of implementation of Import and Export using LDIF
This commit is contained in:
parent
ded1f74285
commit
4c8bd1c81f
@ -168,6 +168,11 @@ class Attribute implements \Countable, \ArrayAccess
|
|||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addValue(string $value): void
|
||||||
|
{
|
||||||
|
$this->values->push($value);
|
||||||
|
}
|
||||||
|
|
||||||
public function count(): int
|
public function count(): int
|
||||||
{
|
{
|
||||||
return $this->values->count();
|
return $this->values->count();
|
||||||
|
@ -50,7 +50,7 @@ class Factory
|
|||||||
*/
|
*/
|
||||||
public static function create(string $attribute,array $values): Attribute
|
public static function create(string $attribute,array $values): Attribute
|
||||||
{
|
{
|
||||||
$class = Arr::get(self::map,$attribute,Attribute::class);
|
$class = Arr::get(self::map,strtolower($attribute),Attribute::class);
|
||||||
Log::debug(sprintf('%s:Creating LDAP Attribute [%s] as [%s]',static::LOGKEY,$attribute,$class));
|
Log::debug(sprintf('%s:Creating LDAP Attribute [%s] as [%s]',static::LOGKEY,$attribute,$class));
|
||||||
|
|
||||||
return new $class($attribute,$values);
|
return new $class($attribute,$values);
|
||||||
|
53
app/Classes/LDAP/Export.php
Normal file
53
app/Classes/LDAP/Export.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use LdapRecord\Query\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export Class
|
||||||
|
*
|
||||||
|
* This abstract classes provides all the common methods and variables for the
|
||||||
|
* export classes.
|
||||||
|
*/
|
||||||
|
abstract class Export
|
||||||
|
{
|
||||||
|
// Line Break
|
||||||
|
protected string $br = "\r\n";
|
||||||
|
|
||||||
|
// Item(s) being Exported
|
||||||
|
protected Collection $items;
|
||||||
|
|
||||||
|
// Type of export
|
||||||
|
protected const type = 'Unknown';
|
||||||
|
|
||||||
|
public function __construct(Collection $items)
|
||||||
|
{
|
||||||
|
$this->items = $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function __toString(): string;
|
||||||
|
|
||||||
|
protected function header()
|
||||||
|
{
|
||||||
|
$output = '';
|
||||||
|
|
||||||
|
$output .= sprintf('# %s %s',__(static::type.' for'),($x=$this->items->first())).$this->br;
|
||||||
|
$output .= sprintf('# %s: %s (%s)',
|
||||||
|
__('Server'),
|
||||||
|
$x->getConnection()->getConfiguration()->get('name'),
|
||||||
|
$x->getConnection()->getLdapConnection()->getHost()).$this->br;
|
||||||
|
//$output .= sprintf('# %s: %s',__('Search Scope'),$this->scope).$this->br;
|
||||||
|
//$output .= sprintf('# %s: %s',__('Search Filter'),$this->entry->dn).$this->br;
|
||||||
|
$output .= sprintf('# %s: %s',__('Total Entries'),$this->items->count()).$this->br;
|
||||||
|
$output .= '#'.$this->br;
|
||||||
|
$output .= sprintf('# %s %s (%s) on %s',__('Generated by'),config('app.name'),config('app.url'),date('F j, Y g:i a')).$this->br;
|
||||||
|
$output .= sprintf('# %s %s',__('Exported by'),Auth::user() ?: 'Anonymous').$this->br;
|
||||||
|
$output .= sprintf('# %s: %s',__('Version'),config('app.version')).$this->br;
|
||||||
|
|
||||||
|
$output .= $this->br;
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
78
app/Classes/LDAP/Export/LDIF.php
Normal file
78
app/Classes/LDAP/Export/LDIF.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Export;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Export;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export from LDAP using an LDIF format
|
||||||
|
*/
|
||||||
|
class LDIF extends Export
|
||||||
|
{
|
||||||
|
// The maximum length of the ldif line
|
||||||
|
private int $line_length = 76;
|
||||||
|
protected const type = 'LDIF Export';
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
$result = parent::header();
|
||||||
|
$result .= 'version: 1';
|
||||||
|
$result .= $this->br;
|
||||||
|
|
||||||
|
$c = 1;
|
||||||
|
foreach ($this->items as $o) {
|
||||||
|
if ($c > 1)
|
||||||
|
$result .= $this->br;
|
||||||
|
|
||||||
|
$title = (string)$o;
|
||||||
|
if (strlen($title) > $this->line_length)
|
||||||
|
$title = Str::of($title)->limit($this->line_length-3-5,'...'.substr($title,-5));
|
||||||
|
|
||||||
|
$result .= sprintf('# %s %s: %s',__('Entry'),$c++,$title).$this->br;
|
||||||
|
|
||||||
|
// Display DN
|
||||||
|
$result .= $this->multiLineDisplay(
|
||||||
|
Str::isAscii($o)
|
||||||
|
? sprintf('dn: %s',$o)
|
||||||
|
: sprintf('dn:: %s',base64_encode($o))
|
||||||
|
,$this->br);
|
||||||
|
|
||||||
|
// Display Attributes
|
||||||
|
foreach ($o->getObjects() as $ao) {
|
||||||
|
foreach ($ao->values as $value) {
|
||||||
|
$result .= $this->multiLineDisplay(
|
||||||
|
Str::isAscii($value)
|
||||||
|
? sprintf('%s: %s',$ao->name,$value)
|
||||||
|
: sprintf('%s:: %s',$ao->name,base64_encode($value))
|
||||||
|
,$this->br);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to wrap LDIF lines
|
||||||
|
*
|
||||||
|
* @param string $str The line to be wrapped if needed.
|
||||||
|
*/
|
||||||
|
private function multiLineDisplay(string $str,string $br): string
|
||||||
|
{
|
||||||
|
$length_string = strlen($str);
|
||||||
|
$length_max = $this->line_length;
|
||||||
|
|
||||||
|
$output = '';
|
||||||
|
while ($length_string > $length_max) {
|
||||||
|
$output .= substr($str,0,$length_max).$br;
|
||||||
|
$str = ' '.substr($str,$length_max);
|
||||||
|
$length_string = strlen($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
$output .= $str.$br;
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
79
app/Classes/LDAP/Import.php
Normal file
79
app/Classes/LDAP/Import.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
use App\Exceptions\Import\GeneralException;
|
||||||
|
use App\Exceptions\Import\ObjectExistsException;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import Class
|
||||||
|
*
|
||||||
|
* This abstract classes provides all the common methods and variables for the
|
||||||
|
* import classes.
|
||||||
|
*/
|
||||||
|
abstract class Import
|
||||||
|
{
|
||||||
|
// Valid LDIF commands
|
||||||
|
protected const LDAP_IMPORT_ADD = 1;
|
||||||
|
protected const LDAP_IMPORT_DELETE = 2;
|
||||||
|
protected const LDAP_IMPORT_MODRDN = 3;
|
||||||
|
protected const LDAP_IMPORT_MODDN = 4;
|
||||||
|
protected const LDAP_IMPORT_MODIFY = 5;
|
||||||
|
|
||||||
|
protected const LDAP_ACTIONS = [
|
||||||
|
'add' => self::LDAP_IMPORT_ADD,
|
||||||
|
'delete' => self::LDAP_IMPORT_DELETE,
|
||||||
|
'modrdn' => self::LDAP_IMPORT_MODRDN,
|
||||||
|
'moddn' => self::LDAP_IMPORT_MODDN,
|
||||||
|
'modify' => self::LDAP_IMPORT_MODIFY,
|
||||||
|
];
|
||||||
|
|
||||||
|
// The import data to process
|
||||||
|
protected string $input;
|
||||||
|
// The attributes the server knows about
|
||||||
|
protected Collection $server_attributes;
|
||||||
|
|
||||||
|
public function __construct(string $input) {
|
||||||
|
$this->input = $input;
|
||||||
|
$this->server_attributes = config('server')->schema('attributetypes');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to commit an entry and return the result.
|
||||||
|
*
|
||||||
|
* @param Entry $o
|
||||||
|
* @param int $action
|
||||||
|
* @return Collection
|
||||||
|
* @throws GeneralException
|
||||||
|
* @throws ObjectExistsException
|
||||||
|
*/
|
||||||
|
final protected function commit(Entry $o,int $action): Collection
|
||||||
|
{
|
||||||
|
switch ($action) {
|
||||||
|
case static::LDAP_IMPORT_ADD:
|
||||||
|
try {
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return collect([
|
||||||
|
'dn'=>$o->getDN(),
|
||||||
|
'result'=>sprintf('%d: %s (%s)',
|
||||||
|
($x=$e->getDetailedError())->getErrorCode(),
|
||||||
|
$x->getErrorMessage(),
|
||||||
|
$x->getDiagnosticMessage(),
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return collect(['dn'=>$o->getDN(),'result'=>__('Created')]);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new GeneralException('Unhandled action during commit: '.$action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function process(): Collection;
|
||||||
|
}
|
233
app/Classes/LDAP/Import/LDIF.php
Normal file
233
app/Classes/LDAP/Import/LDIF.php
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Import;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Nette\NotImplementedException;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Import;
|
||||||
|
use App\Exceptions\Import\{GeneralException,VersionException};
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import LDIF to LDAP using an LDIF format
|
||||||
|
*
|
||||||
|
* The LDIF spec is described by RFC2849
|
||||||
|
* http://www.ietf.org/rfc/rfc2849.txt
|
||||||
|
*/
|
||||||
|
class LDIF extends Import
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'ILF';
|
||||||
|
|
||||||
|
public function process(): Collection
|
||||||
|
{
|
||||||
|
$c = 0;
|
||||||
|
$action = NULL;
|
||||||
|
$attribute = NULL;
|
||||||
|
$base64encoded = FALSE;
|
||||||
|
$o = NULL;
|
||||||
|
$value = '';
|
||||||
|
$version = NULL;
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
// @todo When renaming DNs, the hotlink should point to the new entry on success, or the old entry on failure.
|
||||||
|
foreach (preg_split('/(\r?\n|\r)/',$this->input) as $line) {
|
||||||
|
$c++;
|
||||||
|
Log::debug(sprintf('%s: LDIF Line [%s]',self::LOGKEY,$line));
|
||||||
|
$line = trim($line);
|
||||||
|
|
||||||
|
// If the line starts with a comment, ignore it
|
||||||
|
if (preg_match('/^#/',$line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If we have a blank line, then that completes this command
|
||||||
|
if (! $line) {
|
||||||
|
if (! is_null($o)) {
|
||||||
|
// Add the last attribute;
|
||||||
|
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||||
|
|
||||||
|
// Commit
|
||||||
|
$result->push($this->commit($o,$action));
|
||||||
|
$result->last()->put('line',$c);
|
||||||
|
|
||||||
|
$o = NULL;
|
||||||
|
$action = NULL;
|
||||||
|
$base64encoded = FALSE;
|
||||||
|
$attribute = NULL;
|
||||||
|
$value = '';
|
||||||
|
|
||||||
|
// Else its a blank line
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$m = [];
|
||||||
|
preg_match('/^([a-zA-Z0-9;-]+)(:+)\s+(.*)$/',$line,$m);
|
||||||
|
|
||||||
|
switch ($x=Arr::get($m,1)) {
|
||||||
|
case 'changetype':
|
||||||
|
if ($m[2] !== ':')
|
||||||
|
throw new GeneralException(sprintf('ChangeType cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
|
||||||
|
|
||||||
|
switch ($m[3]) {
|
||||||
|
// if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) {
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException(sprintf('Unknown change type [%s]? (line %d)',$m[3],$c));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'version':
|
||||||
|
if (! is_null($version))
|
||||||
|
throw new VersionException(sprintf('Version has already been set at [%d]. (line %d)',$version,$c));
|
||||||
|
|
||||||
|
if ($m[2] !== ':')
|
||||||
|
throw new VersionException(sprintf('Version cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
|
||||||
|
|
||||||
|
$version = (int)$m[3];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Treat it as an attribute
|
||||||
|
default:
|
||||||
|
// If $m is NULL, then this is the 2nd (or more) line of a base64 encoded value
|
||||||
|
if (! $m) {
|
||||||
|
$value .= $line;
|
||||||
|
Log::debug(sprintf('%s: Attribute [%s] adding [%s] (%d)',self::LOGKEY,$attribute,$line,$c));
|
||||||
|
|
||||||
|
// add to last attr value
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are ready to create the entry or add the attribute
|
||||||
|
if ($attribute) {
|
||||||
|
if ($attribute === 'dn') {
|
||||||
|
if (! is_null($o))
|
||||||
|
throw new GeneralException(sprintf('Previous Entry not complete? (line %d)',$c));
|
||||||
|
|
||||||
|
$dn = $base64encoded ? base64_decode($value) : $value;
|
||||||
|
Log::debug(sprintf('%s: Creating new entry:',self::LOGKEY,$dn));
|
||||||
|
//$o = Entry::find($dn);
|
||||||
|
|
||||||
|
// If it doesnt exist, we'll create it
|
||||||
|
//if (! $o) {
|
||||||
|
$o = new Entry;
|
||||||
|
$o->setDn($dn);
|
||||||
|
//}
|
||||||
|
|
||||||
|
$action = self::LDAP_IMPORT_ADD;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s: Adding Attribute [%s] value [%s] (%d)',self::LOGKEY,$attribute,$value,$c));
|
||||||
|
|
||||||
|
if ($value)
|
||||||
|
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||||
|
else
|
||||||
|
throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start of a new attribute
|
||||||
|
$base64encoded = ($m[2] === '::');
|
||||||
|
// @todo Need to parse attributes with ';' options
|
||||||
|
$attribute = $m[1];
|
||||||
|
$value = $m[3];
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: New Attribute [%s] with [%s] (%d)',self::LOGKEY,$attribute,$value,$c));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($version !== 1)
|
||||||
|
throw new VersionException('LDIF import cannot handle version: '.($version ?: __('NOT DEFINED')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We may still have a pending action
|
||||||
|
if ($action) {
|
||||||
|
// Add the last attribute;
|
||||||
|
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||||
|
|
||||||
|
// Commit
|
||||||
|
$result->push($this->commit($o,$action));
|
||||||
|
$result->last()->put('line',$c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readEntry() {
|
||||||
|
static $haveVersion = false;
|
||||||
|
|
||||||
|
if ($lines = $this->nextLines()) {
|
||||||
|
|
||||||
|
$server = $this->getServer();
|
||||||
|
|
||||||
|
# The first line should be the DN
|
||||||
|
if (preg_match('/^dn:/',$lines[0])) {
|
||||||
|
list($text,$dn) = $this->getAttrValue(array_shift($lines));
|
||||||
|
|
||||||
|
# The second line should be our changetype
|
||||||
|
if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) {
|
||||||
|
$attrvalue = $this->getAttrValue($lines[0]);
|
||||||
|
$changetype = $attrvalue[1];
|
||||||
|
array_shift($lines);
|
||||||
|
|
||||||
|
} else
|
||||||
|
$changetype = 'add';
|
||||||
|
|
||||||
|
$this->template = new Template($this->server_id,null,null,$changetype);
|
||||||
|
|
||||||
|
switch ($changetype) {
|
||||||
|
case 'add':
|
||||||
|
$rdn = get_rdn($dn);
|
||||||
|
$container = $server->getContainer($dn);
|
||||||
|
|
||||||
|
$this->template->setContainer($container);
|
||||||
|
$this->template->accept();
|
||||||
|
|
||||||
|
$this->getAddDetails($lines);
|
||||||
|
$this->template->setRDNAttributes($rdn);
|
||||||
|
|
||||||
|
return $this->template;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'modify':
|
||||||
|
if (! $server->dnExists($dn))
|
||||||
|
return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines);
|
||||||
|
|
||||||
|
$this->template->setDN($dn);
|
||||||
|
$this->template->accept(false,true);
|
||||||
|
|
||||||
|
return $this->getModifyDetails($lines);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'moddn':
|
||||||
|
case 'modrdn':
|
||||||
|
if (! $server->dnExists($dn))
|
||||||
|
return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines);
|
||||||
|
|
||||||
|
$this->template->setDN($dn);
|
||||||
|
$this->template->accept();
|
||||||
|
|
||||||
|
return $this->getModRDNAttributes($lines);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (! $server->dnExists($dn))
|
||||||
|
return $this->error(_('Unkown change type'),$lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
return $this->error(_('A valid dn line is required'),$lines);
|
||||||
|
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
7
app/Exceptions/Import/AttributeException.php
Normal file
7
app/Exceptions/Import/AttributeException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Import;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class AttributeException extends Exception {}
|
7
app/Exceptions/Import/GeneralException.php
Normal file
7
app/Exceptions/Import/GeneralException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Import;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class GeneralException extends Exception {}
|
7
app/Exceptions/Import/ObjectExistsException.php
Normal file
7
app/Exceptions/Import/ObjectExistsException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Import;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class ObjectExistsException extends Exception {}
|
7
app/Exceptions/Import/VersionException.php
Normal file
7
app/Exceptions/Import/VersionException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions\Import;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class VersionException extends Exception {}
|
@ -8,19 +8,38 @@ use Illuminate\Support\Collection;
|
|||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Redirect;
|
use Illuminate\Support\Facades\Redirect;
|
||||||
use LdapRecord\Exceptions\InsufficientAccessException;
|
use LdapRecord\Exceptions\InsufficientAccessException;
|
||||||
use LdapRecord\LdapRecordException;
|
use LdapRecord\LdapRecordException;
|
||||||
use LdapRecord\Query\ObjectNotFoundException;
|
use LdapRecord\Query\ObjectNotFoundException;
|
||||||
|
|
||||||
use App\Classes\LDAP\{Attribute,Server};
|
use App\Classes\LDAP\{Attribute,Server};
|
||||||
|
use App\Classes\LDAP\Import\LDIF as LDIFImport;
|
||||||
|
use App\Classes\LDAP\Export\LDIF as LDIFExport;
|
||||||
|
use App\Exceptions\Import\{GeneralException,VersionException};
|
||||||
use App\Exceptions\InvalidUsage;
|
use App\Exceptions\InvalidUsage;
|
||||||
use App\Http\Requests\EntryRequest;
|
use App\Http\Requests\{EntryRequest,ImportRequest};
|
||||||
|
use App\Ldap\Entry;
|
||||||
use App\View\Components\AttributeType;
|
use App\View\Components\AttributeType;
|
||||||
|
use Nette\NotImplementedException;
|
||||||
|
|
||||||
class HomeController extends Controller
|
class HomeController extends Controller
|
||||||
{
|
{
|
||||||
|
private function bases()
|
||||||
|
{
|
||||||
|
$base = Server::baseDNs() ?: collect();
|
||||||
|
|
||||||
|
return $base->transform(function($item) {
|
||||||
|
return [
|
||||||
|
'title'=>$item->getRdn(),
|
||||||
|
'item'=>$item->getDNSecure(),
|
||||||
|
'lazy'=>TRUE,
|
||||||
|
'icon'=>'fa-fw fas fa-sitemap',
|
||||||
|
'tooltip'=>$item->getDn(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug Page
|
* Debug Page
|
||||||
*
|
*
|
||||||
@ -49,6 +68,22 @@ class HomeController extends Controller
|
|||||||
->with('page_actions',$page_actions);
|
->with('page_actions',$page_actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function entry_export(Request $request,string $id)
|
||||||
|
{
|
||||||
|
$dn = Crypt::decryptString($id);
|
||||||
|
|
||||||
|
$result = (new Entry)
|
||||||
|
->query()
|
||||||
|
//->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
||||||
|
//->select(['*'])
|
||||||
|
->setDn($dn)
|
||||||
|
->recursive()
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('fragment.export')
|
||||||
|
->with('result',new LDIFExport($result));
|
||||||
|
}
|
||||||
|
|
||||||
public function entry_newattr(string $id)
|
public function entry_newattr(string $id)
|
||||||
{
|
{
|
||||||
$x = new AttributeType(new Attribute($id,[]),TRUE);
|
$x = new AttributeType(new Attribute($id,[]),TRUE);
|
||||||
@ -76,20 +111,8 @@ class HomeController extends Controller
|
|||||||
->withInput()
|
->withInput()
|
||||||
->with('note',__('No attributes changed'));
|
->with('note',__('No attributes changed'));
|
||||||
|
|
||||||
$base = Server::baseDNs() ?: collect();
|
return view('update')
|
||||||
|
->with('bases',$this->bases())
|
||||||
$bases = $base->transform(function($item) {
|
|
||||||
return [
|
|
||||||
'title'=>$item->getRdn(),
|
|
||||||
'item'=>$item->getDNSecure(),
|
|
||||||
'lazy'=>TRUE,
|
|
||||||
'icon'=>'fa-fw fas fa-sitemap',
|
|
||||||
'tooltip'=>$item->getDn(),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
return view('frames.update')
|
|
||||||
->with('bases',$bases)
|
|
||||||
->with('dn',$dn)
|
->with('dn',$dn)
|
||||||
->with('o',$o);
|
->with('o',$o);
|
||||||
}
|
}
|
||||||
@ -103,18 +126,6 @@ class HomeController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function entry_update(EntryRequest $request)
|
public function entry_update(EntryRequest $request)
|
||||||
{
|
{
|
||||||
$base = Server::baseDNs() ?: collect();
|
|
||||||
|
|
||||||
$bases = $base->transform(function($item) {
|
|
||||||
return [
|
|
||||||
'title'=>$item->getRdn(),
|
|
||||||
'item'=>$item->getDNSecure(),
|
|
||||||
'lazy'=>TRUE,
|
|
||||||
'icon'=>'fa-fw fas fa-sitemap',
|
|
||||||
'tooltip'=>$item->getDn(),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
$dn = Crypt::decryptString($request->dn);
|
$dn = Crypt::decryptString($request->dn);
|
||||||
|
|
||||||
$o = config('server')->fetch($dn);
|
$o = config('server')->fetch($dn);
|
||||||
@ -168,51 +179,75 @@ class HomeController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function home()
|
public function home()
|
||||||
{
|
{
|
||||||
$base = Server::baseDNs() ?: collect();
|
|
||||||
|
|
||||||
$bases = $base->transform(function($item) {
|
|
||||||
return [
|
|
||||||
'title'=>$item->getRdn(),
|
|
||||||
'item'=>$item->getDNSecure(),
|
|
||||||
'lazy'=>TRUE,
|
|
||||||
'icon'=>'fa-fw fas fa-sitemap',
|
|
||||||
'tooltip'=>$item->getDn(),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (old('dn'))
|
if (old('dn'))
|
||||||
return view('frame')
|
return view('frame')
|
||||||
->with('subframe','dn')
|
->with('subframe','dn')
|
||||||
->with('bases',$bases)
|
->with('bases',$this->bases())
|
||||||
->with('o',config('server')->fetch($dn=Crypt::decryptString(old('dn'))))
|
->with('o',config('server')->fetch($dn=Crypt::decryptString(old('dn'))))
|
||||||
->with('dn',$dn);
|
->with('dn',$dn);
|
||||||
|
|
||||||
elseif (old('frame'))
|
elseif (old('frame'))
|
||||||
return view('frame')
|
return view('frame')
|
||||||
->with('subframe',old('frame'))
|
->with('subframe',old('frame'))
|
||||||
->with('bases',$bases);
|
->with('bases',$this->bases());
|
||||||
|
|
||||||
else
|
else
|
||||||
return view('home')
|
return view('home')
|
||||||
->with('bases',$bases)
|
->with('bases',$this->bases())
|
||||||
->with('server',config('ldap.connections.default.name'));
|
->with('server',config('ldap.connections.default.name'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the incoming LDIF file or LDIF text
|
||||||
|
*
|
||||||
|
* @param ImportRequest $request
|
||||||
|
* @param string $type
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application
|
||||||
|
* @throws GeneralException
|
||||||
|
* @throws VersionException
|
||||||
|
*/
|
||||||
|
public function import(ImportRequest $request,string $type)
|
||||||
|
{
|
||||||
|
switch ($type) {
|
||||||
|
case 'ldif':
|
||||||
|
$import = new LDIFImport($x=($request->text ?: $request->file->get()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort(404,'Unknown import type: '.$type);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $import->process();
|
||||||
|
|
||||||
|
} catch (NotImplementedException $e) {
|
||||||
|
abort(555,$e->getMessage());
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
abort(598,$e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('frame')
|
||||||
|
->with('subframe','import_result')
|
||||||
|
->with('bases',$this->bases())
|
||||||
|
->with('result',$result)
|
||||||
|
->with('ldif',htmlspecialchars($x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function import_frame()
|
||||||
|
{
|
||||||
|
return view('frames.import');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LDAP Server INFO
|
* LDAP Server INFO
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
* @throws ObjectNotFoundException
|
|
||||||
*/
|
*/
|
||||||
public function info()
|
public function info()
|
||||||
{
|
{
|
||||||
// Load our attributes
|
|
||||||
$s = config('server');
|
|
||||||
$s->schema('objectclasses');
|
|
||||||
$s->schema('attributetypes');
|
|
||||||
|
|
||||||
return view('frames.info')
|
return view('frames.info')
|
||||||
->with('s',$s);
|
->with('s',config('server'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
22
app/Http/Requests/ImportRequest.php
Normal file
22
app/Http/Requests/ImportRequest.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class ImportRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'frame' => 'required|string|in:import',
|
||||||
|
'file' => 'nullable|extensions:ldif|required_without:text',
|
||||||
|
'text'=> 'nullable|prohibits:file|string|min:16',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -2,21 +2,50 @@
|
|||||||
|
|
||||||
namespace App\Ldap;
|
namespace App\Ldap;
|
||||||
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use LdapRecord\Support\Arr;
|
||||||
use LdapRecord\Models\Model;
|
use LdapRecord\Models\Model;
|
||||||
|
use LdapRecord\Query\Model\Builder;
|
||||||
|
|
||||||
use App\Classes\LDAP\Attribute;
|
use App\Classes\LDAP\Attribute;
|
||||||
use App\Classes\LDAP\Attribute\Factory;
|
use App\Classes\LDAP\Attribute\Factory;
|
||||||
|
use App\Classes\LDAP\Export\LDIF;
|
||||||
|
use App\Exceptions\Import\AttributeException;
|
||||||
|
|
||||||
class Entry extends Model
|
class Entry extends Model
|
||||||
{
|
{
|
||||||
|
private Collection $objects;
|
||||||
|
private bool $noObjectAttributes = FALSE;
|
||||||
|
|
||||||
/* OVERRIDES */
|
/* OVERRIDES */
|
||||||
|
|
||||||
|
public function __construct(array $attributes = [])
|
||||||
|
{
|
||||||
|
$this->objects = collect();
|
||||||
|
|
||||||
|
parent::__construct($attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function discardChanges(): static
|
||||||
|
{
|
||||||
|
parent::discardChanges();
|
||||||
|
|
||||||
|
// If we are discharging changes, we need to reset our $objects;
|
||||||
|
$this->objects = $this->getAttributesAsObjects($this->attributes);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function overrides getAttributes to use our collection of Attribute objects instead of the models attributes.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @note $this->attributes may not be updated with changes
|
||||||
|
*/
|
||||||
public function getAttributes(): array
|
public function getAttributes(): array
|
||||||
{
|
{
|
||||||
return $this->getAttributesAsObjects()->toArray();
|
return $this->objects->map(function($item) { return $item->values->toArray(); })->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,57 +53,80 @@ class Entry extends Model
|
|||||||
*/
|
*/
|
||||||
protected function originalIsEquivalent(string $key): bool
|
protected function originalIsEquivalent(string $key): bool
|
||||||
{
|
{
|
||||||
if (! array_key_exists($key, $this->original)) {
|
$key = $this->normalizeAttributeKey($key);
|
||||||
return false;
|
|
||||||
|
if ((! array_key_exists($key, $this->original)) && (! $this->objects->has($key))) {
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
$current = $this->attributes[$key];
|
$current = $this->attributes[$key];
|
||||||
$original = $this->original[$key];
|
$original = $this->objects->get($key)->values;
|
||||||
|
|
||||||
if ($current === $original) {
|
if ($current === $original) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//dump(['key'=>$key,'current'=>$current,'original'=>$this->original[$key],'objectvalue'=>$this->getAttributeAsObject($key)->isDirty()]);
|
return ! $this->getObject($key)->isDirty();
|
||||||
return ! $this->getAttributeAsObject($key)->isDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOriginal(): array
|
public static function query(bool $noattrs=false): Builder
|
||||||
{
|
{
|
||||||
static $result = NULL;
|
$o = new static;
|
||||||
|
|
||||||
if (is_null($result)) {
|
if ($noattrs)
|
||||||
$result = collect();
|
$o->noObjectAttributes();
|
||||||
|
|
||||||
// @todo Optimise this foreach with getAttributes()
|
return $o->newQuery();
|
||||||
foreach (parent::getOriginal() as $attribute => $value) {
|
}
|
||||||
// If the attribute name has language tags
|
|
||||||
$matches = [];
|
|
||||||
if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
|
|
||||||
$attribute = $matches[1];
|
|
||||||
|
|
||||||
// If the attribute doesnt exist we'll create it
|
/**
|
||||||
$o = Arr::get($result,$attribute,Factory::create($attribute,[]));
|
* As attribute values are updated, or new ones created, we need to mirror that
|
||||||
$o->setLangTag($matches[3],$value);
|
* into our $objects
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setAttribute(string $key, mixed $value): static
|
||||||
|
{
|
||||||
|
parent::setAttribute($key,$value);
|
||||||
|
|
||||||
} else {
|
$key = $this->normalizeAttributeKey($key);
|
||||||
$o = Factory::create($attribute,$value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $result->has($attribute)) {
|
if ((! $this->objects->get($key)) && $value) {
|
||||||
// Set the rdn flag
|
$o = new Attribute($key,[]);
|
||||||
if (preg_match('/^'.$attribute.'=/i',$this->dn))
|
$o->value = $value;
|
||||||
$o->setRDN();
|
|
||||||
|
|
||||||
// Set required flag
|
$this->objects->put($key,$o);
|
||||||
$o->required_by(collect($this->getAttribute('objectclass')));
|
|
||||||
|
|
||||||
$result->put($attribute,$o);
|
} elseif ($this->objects->get($key)) {
|
||||||
}
|
$this->objects->get($key)->value = $this->attributes[$key];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result->toArray();
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We'll shadow $this->attributes to $this->objects - a collection of Attribute objects
|
||||||
|
*
|
||||||
|
* Using the objects, it'll make it easier to work with attribute values
|
||||||
|
*
|
||||||
|
* @param array $attributes
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRawAttributes(array $attributes = []): static
|
||||||
|
{
|
||||||
|
parent::setRawAttributes($attributes);
|
||||||
|
|
||||||
|
// We only set our objects on DN entries (otherwise we might get into a recursion loop if this is the schema DN)
|
||||||
|
if ($this->dn && (! in_array($this->dn,Arr::get($this->attributes,'subschemasubentry',[])))) {
|
||||||
|
$this->objects = $this->getAttributesAsObjects($this->attributes);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->objects = collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ATTRIBUTES */
|
/* ATTRIBUTES */
|
||||||
@ -92,88 +144,89 @@ class Entry extends Model
|
|||||||
|
|
||||||
/* METHODS */
|
/* METHODS */
|
||||||
|
|
||||||
/**
|
public function addAttribute(string $key,mixed $value): void
|
||||||
* Get an attribute as an object
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @return Attribute|null
|
|
||||||
*/
|
|
||||||
public function getAttributeAsObject(string $key): Attribute|null
|
|
||||||
{
|
{
|
||||||
return Arr::get($this->getAttributesAsObjects(),$key);
|
$key = $this->normalizeAttributeKey($key);
|
||||||
|
|
||||||
|
if (config('server')->schema('attributetypes')->has($key) === FALSE)
|
||||||
|
throw new AttributeException('Schema doesnt have attribute [%s]',$key);
|
||||||
|
|
||||||
|
if ($x=$this->objects->get($key)) {
|
||||||
|
$x->addValue($value);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->objects->put($key,Attribute\Factory::create($key,Arr::wrap($value)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert all our attribute values into an array of Objects
|
* Convert all our attribute values into an array of Objects
|
||||||
*
|
*
|
||||||
|
* @param array $attributes
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
protected function getAttributesAsObjects(): Collection
|
protected function getAttributesAsObjects(array $attributes): Collection
|
||||||
{
|
{
|
||||||
static $result = NULL;
|
$result = collect();
|
||||||
|
|
||||||
if (is_null($result)) {
|
foreach ($attributes as $attribute => $value) {
|
||||||
$result = collect();
|
// If the attribute name has language tags
|
||||||
|
$matches = [];
|
||||||
|
if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
|
||||||
|
$attribute = $matches[1];
|
||||||
|
|
||||||
foreach (parent::getAttributes() as $attribute => $value) {
|
// If the attribute doesnt exist we'll create it
|
||||||
// If the attribute name has language tags
|
$o = Arr::get($result,$attribute,Factory::create($attribute,[]));
|
||||||
$matches = [];
|
$o->setLangTag($matches[3],$value);
|
||||||
if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
|
|
||||||
$attribute = $matches[1];
|
|
||||||
|
|
||||||
// If the attribute doesnt exist we'll create it
|
} else {
|
||||||
$o = Arr::get($result,$attribute,Factory::create($attribute,[]));
|
$o = Factory::create($attribute,$value);
|
||||||
$o->setLangTag($matches[3],$value);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$o = Factory::create($attribute,$value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $result->has($attribute)) {
|
|
||||||
// Set the rdn flag
|
|
||||||
if (preg_match('/^'.$attribute.'=/i',$this->dn))
|
|
||||||
$o->setRDN();
|
|
||||||
|
|
||||||
// Set required flag
|
|
||||||
$o->required_by(collect($this->getAttribute('objectclass')));
|
|
||||||
|
|
||||||
// Store our original value to know if this attribute has changed
|
|
||||||
if ($x=Arr::get($this->original,$attribute))
|
|
||||||
$o->oldValues($x);
|
|
||||||
|
|
||||||
$result->put($attribute,$o);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$sort = collect(config('ldap.attr_display_order',[]))->transform(function($item) { return strtolower($item); });
|
if (! $result->has($attribute)) {
|
||||||
|
// Set the rdn flag
|
||||||
|
if (preg_match('/^'.$attribute.'=/i',$this->dn))
|
||||||
|
$o->setRDN();
|
||||||
|
|
||||||
// Order the attributes
|
// Set required flag
|
||||||
$result = $result->sortBy([function(Attribute $a,Attribute $b) use ($sort): int {
|
$o->required_by(collect($this->getAttribute('objectclass')));
|
||||||
if ($a === $b)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Check if $a/$b are in the configuration to be sorted first, if so get it's key
|
// Store our original value to know if this attribute has changed
|
||||||
$a_key = $sort->search($a->name_lc);
|
if ($x=Arr::get($this->original,$attribute))
|
||||||
$b_key = $sort->search($b->name_lc);
|
$o->oldValues($x);
|
||||||
|
|
||||||
// If the keys were not in the sort list, set the key to be the count of elements (ie: so it is last to be sorted)
|
$result->put($attribute,$o);
|
||||||
if ($a_key === FALSE)
|
}
|
||||||
$a_key = $sort->count()+1;
|
|
||||||
|
|
||||||
if ($b_key === FALSE)
|
|
||||||
$b_key = $sort->count()+1;
|
|
||||||
|
|
||||||
// Case where neither $a, nor $b are in ldap.attr_display_order, $a_key = $b_key = one greater than num elements.
|
|
||||||
// So we sort them alphabetically
|
|
||||||
if ($a_key === $b_key)
|
|
||||||
return strcasecmp($a->name,$b->name);
|
|
||||||
|
|
||||||
// Case where at least one attribute or its friendly name is in $attrs_display_order
|
|
||||||
// return -1 if $a before $b in $attrs_display_order
|
|
||||||
return ($a_key < $b_key) ? -1 : 1;
|
|
||||||
} ]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sort = collect(config('ldap.attr_display_order',[]))->transform(function($item) { return strtolower($item); });
|
||||||
|
|
||||||
|
// Order the attributes
|
||||||
|
$result = $result->sortBy([function(Attribute $a,Attribute $b) use ($sort): int {
|
||||||
|
if ($a === $b)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Check if $a/$b are in the configuration to be sorted first, if so get it's key
|
||||||
|
$a_key = $sort->search($a->name_lc);
|
||||||
|
$b_key = $sort->search($b->name_lc);
|
||||||
|
|
||||||
|
// If the keys were not in the sort list, set the key to be the count of elements (ie: so it is last to be sorted)
|
||||||
|
if ($a_key === FALSE)
|
||||||
|
$a_key = $sort->count()+1;
|
||||||
|
|
||||||
|
if ($b_key === FALSE)
|
||||||
|
$b_key = $sort->count()+1;
|
||||||
|
|
||||||
|
// Case where neither $a, nor $b are in ldap.attr_display_order, $a_key = $b_key = one greater than num elements.
|
||||||
|
// So we sort them alphabetically
|
||||||
|
if ($a_key === $b_key)
|
||||||
|
return strcasecmp($a->name,$b->name);
|
||||||
|
|
||||||
|
// Case where at least one attribute or its friendly name is in $attrs_display_order
|
||||||
|
// return -1 if $a before $b in $attrs_display_order
|
||||||
|
return ($a_key < $b_key) ? -1 : 1;
|
||||||
|
} ]);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,11 +261,31 @@ class Entry extends Model
|
|||||||
*/
|
*/
|
||||||
public function getInternalAttributes(): Collection
|
public function getInternalAttributes(): Collection
|
||||||
{
|
{
|
||||||
return collect($this->getAttributes())->filter(function($item) {
|
return $this->objects->filter(function($item) {
|
||||||
return $item->is_internal;
|
return $item->is_internal;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an attribute as an object
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return Attribute|null
|
||||||
|
*/
|
||||||
|
public function getObject(string $key): Attribute|null
|
||||||
|
{
|
||||||
|
return $this->objects->get($this->normalizeAttributeKey($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjects(): Collection
|
||||||
|
{
|
||||||
|
// In case we havent built our objects yet (because they werent available while determining the schema DN)
|
||||||
|
if ((! $this->objects->count()) && $this->attributes)
|
||||||
|
$this->objects = $this->getAttributesAsObjects($this->attributes);
|
||||||
|
|
||||||
|
return $this->objects;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of attributes without any values
|
* Return a list of attributes without any values
|
||||||
*
|
*
|
||||||
@ -230,11 +303,46 @@ class Entry extends Model
|
|||||||
*/
|
*/
|
||||||
public function getVisibleAttributes(): Collection
|
public function getVisibleAttributes(): Collection
|
||||||
{
|
{
|
||||||
return collect($this->getAttributes())->filter(function($item) {
|
return $this->objects->filter(function($item) {
|
||||||
return ! $item->is_internal;
|
return ! $item->is_internal;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasAttribute(int|string $key): bool
|
||||||
|
{
|
||||||
|
return $this->objects->has($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export this record
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $scope
|
||||||
|
* @return string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function export(string $method,string $scope): string
|
||||||
|
{
|
||||||
|
// @todo To implement
|
||||||
|
switch ($scope) {
|
||||||
|
case 'base':
|
||||||
|
case 'one':
|
||||||
|
case 'sub':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('Export scope unknown:'.$scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($method) {
|
||||||
|
case 'ldif':
|
||||||
|
return new LDIF(collect($this));
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('Export method not implemented:'.$method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an icon for a DN based on objectClass
|
* Return an icon for a DN based on objectClass
|
||||||
*
|
*
|
||||||
@ -300,4 +408,16 @@ class Entry extends Model
|
|||||||
// Default
|
// Default
|
||||||
return 'fa-fw fas fa-cog';
|
return 'fa-fw fas fa-cog';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dont convert our $this->attributes to $this->objects when creating a new Entry::class
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function noObjectAttributes(): static
|
||||||
|
{
|
||||||
|
$this->noObjectAttributes = TRUE;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Performs the export of data from the LDAP server
|
|
||||||
*
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @subpackage Page
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
|
|
||||||
require './common.php';
|
|
||||||
require LIBDIR.'export_functions.php';
|
|
||||||
|
|
||||||
# Prevent script from bailing early for long search
|
|
||||||
@set_time_limit(0);
|
|
||||||
|
|
||||||
$request = array();
|
|
||||||
$request['file'] = get_request('save_as_file') ? true : false;
|
|
||||||
$request['exporter'] = new Exporter($app['server']->getIndex(),get_request('exporter_id','REQUEST'));
|
|
||||||
$request['export'] = $request['exporter']->getTemplate();
|
|
||||||
$types = $request['export']->getType();
|
|
||||||
|
|
||||||
# send the header
|
|
||||||
if ($request['file']) {
|
|
||||||
$obStatus = ob_get_status();
|
|
||||||
if (isset($obStatus['type']) && $obStatus['type'] && $obStatus['status'])
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
header('Content-type: application/download');
|
|
||||||
header(sprintf('Content-Disposition: inline; filename="%s.%s"','export',$types['extension'].($request['export']->isCompressed() ? '.gz' : '')));
|
|
||||||
echo $request['export']->export();
|
|
||||||
die();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
print '<span style="font-size: 14px; font-family: courier;"><pre>';
|
|
||||||
echo htmlspecialchars($request['export']->export());
|
|
||||||
print '</pre></span>';
|
|
||||||
}
|
|
||||||
?>
|
|
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Displays a form to allow the user to upload and import
|
|
||||||
* an LDIF file.
|
|
||||||
*
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @subpackage Page
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
|
|
||||||
require './common.php';
|
|
||||||
|
|
||||||
if (! ini_get('file_uploads'))
|
|
||||||
error(_('Your PHP.INI does not have file_uploads = ON. Please enable file uploads in PHP.'),'error','index.php');
|
|
||||||
|
|
||||||
$request['page'] = new PageRender($app['server']->getIndex(),get_request('template','REQUEST',false,'none'));
|
|
||||||
$request['page']->drawTitle(sprintf('<b>%s</b>',_('Import')));
|
|
||||||
$request['page']->drawSubTitle(sprintf('%s: <b>%s</b>',_('Server'),$app['server']->getName()));
|
|
||||||
|
|
||||||
echo '<form action="cmd.php" method="post" class="new_value" enctype="multipart/form-data">';
|
|
||||||
echo '<div>';
|
|
||||||
printf('<input type="hidden" name="server_id" value="%s" />',$app['server']->getIndex());
|
|
||||||
echo '<input type="hidden" name="cmd" value="import" />';
|
|
||||||
echo '</div>';
|
|
||||||
|
|
||||||
echo '<table class="forminput" border="0" style="margin-left: auto; margin-right: auto;">';
|
|
||||||
|
|
||||||
echo '<tr><td colspan="2"> </td></tr>';
|
|
||||||
echo '<tr>';
|
|
||||||
printf('<td>%s</td>',_('Select an LDIF file'));
|
|
||||||
echo '<td>';
|
|
||||||
echo '<input type="file" name="ldif_file" />';
|
|
||||||
echo '</td></tr>';
|
|
||||||
|
|
||||||
printf('<tr><td> </td><td class="small"><b>%s %s</b></td></tr>',_('Maximum file size'),ini_get('upload_max_filesize'));
|
|
||||||
|
|
||||||
echo '<tr><td colspan="2"> </td></tr>';
|
|
||||||
printf('<tr><td>%s</td></tr>',_('Or paste your LDIF here'));
|
|
||||||
echo '<tr><td colspan="2"><textarea name="ldif" rows="20" cols="100"></textarea></td></tr>';
|
|
||||||
echo '<tr><td colspan="2"> </td></tr>';
|
|
||||||
printf('<tr><td> </td><td class="small"><input type="checkbox" name="continuous_mode" value="1" />%s</td></tr>',
|
|
||||||
_("Don't stop on errors"));
|
|
||||||
printf('<tr><td> </td><td><input type="submit" value="%s" /></td></tr>',_('Proceed >>'));
|
|
||||||
echo '</table>';
|
|
||||||
echo '</form>';
|
|
||||||
?>
|
|
@ -1,643 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Classes and functions for export data from LDAP
|
|
||||||
*
|
|
||||||
* These classes provide differnet export formats.
|
|
||||||
*
|
|
||||||
* @author The phpLDAPadmin development team
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @see export.php and export_form.php
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exporter Class
|
|
||||||
*
|
|
||||||
* This class serves as a top level exporter class, which will return
|
|
||||||
* the correct Export class.
|
|
||||||
*
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @subpackage Export
|
|
||||||
*/
|
|
||||||
class Exporter {
|
|
||||||
# Server ID that the export is linked to
|
|
||||||
private $server_id;
|
|
||||||
# Exporter Type
|
|
||||||
private $template_id;
|
|
||||||
private $template;
|
|
||||||
|
|
||||||
public function __construct($server_id,$template_id) {
|
|
||||||
$this->server_id = $server_id;
|
|
||||||
$this->template_id = $template_id;
|
|
||||||
|
|
||||||
$this->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
static function types() {
|
|
||||||
$type = array();
|
|
||||||
|
|
||||||
$details = ExportCSV::getType();
|
|
||||||
$type[$details['type']] = $details;
|
|
||||||
$details = ExportDSML::getType();
|
|
||||||
$type[$details['type']] = $details;
|
|
||||||
$details = ExportLDIF::getType();
|
|
||||||
$type[$details['type']] = $details;
|
|
||||||
$details = ExportVCARD::getType();
|
|
||||||
$type[$details['type']] = $details;
|
|
||||||
|
|
||||||
return $type;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function accept() {
|
|
||||||
switch($this->template_id) {
|
|
||||||
case 'CSV':
|
|
||||||
$this->template = new ExportCSV();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'DSML':
|
|
||||||
$this->template = new ExportDSML();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'LDIF':
|
|
||||||
$this->template = new ExportLDIF();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'VCARD':
|
|
||||||
$this->template = new ExportVCARD();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
system_message(array(
|
|
||||||
'title'=>sprintf('%s %s',_('Unknown Export Type'),$this->template_id),
|
|
||||||
'body'=>_('phpLDAPadmin has not been configured for that export type'),
|
|
||||||
'type'=>'warn'),'index.php');
|
|
||||||
die();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->template->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTemplate() {
|
|
||||||
return $this->template;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export Class
|
|
||||||
*
|
|
||||||
* This abstract classes provides all the common methods and variables for the
|
|
||||||
* custom export classes.
|
|
||||||
*
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @subpackage Export
|
|
||||||
*/
|
|
||||||
abstract class Export {
|
|
||||||
# Line Break
|
|
||||||
protected $br;
|
|
||||||
# Compress the output
|
|
||||||
protected $compress;
|
|
||||||
# Export Results
|
|
||||||
protected $results;
|
|
||||||
protected $resultsdata;
|
|
||||||
protected $items = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return this LDAP Server object
|
|
||||||
*
|
|
||||||
* @return object DataStore Server
|
|
||||||
*/
|
|
||||||
protected function getServer() {
|
|
||||||
return $_SESSION[APPCONFIG]->getServer($this->getServerID());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the LDAP server ID
|
|
||||||
*
|
|
||||||
* @return int Server ID
|
|
||||||
*/
|
|
||||||
protected function getServerID() {
|
|
||||||
return get_request('server_id','REQUEST');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function accept() {
|
|
||||||
$server = $this->getServer();
|
|
||||||
|
|
||||||
# Get the data to be exported
|
|
||||||
$query = array();
|
|
||||||
$base = get_request('dn','REQUEST');
|
|
||||||
$query['baseok'] = true;
|
|
||||||
$query['filter'] = get_request('filter','REQUEST',false,'objectclass=*');
|
|
||||||
$query['scope'] = get_request('scope','REQUEST',false,'base');
|
|
||||||
$query['deref'] = $_SESSION[APPCONFIG]->getValue('deref','export');
|
|
||||||
$query['size_limit'] = 0;
|
|
||||||
$attrs = get_request('attributes','REQUEST');
|
|
||||||
|
|
||||||
$attrs = preg_replace('/\s+/','',$attrs);
|
|
||||||
if ($attrs)
|
|
||||||
$query['attrs'] = explode(',',$attrs);
|
|
||||||
else
|
|
||||||
$query['attrs'] = array('*');
|
|
||||||
|
|
||||||
if (get_request('sys_attr')) {
|
|
||||||
if (! in_array('*',$query['attrs']))
|
|
||||||
array_push($query['attrs'],'*');
|
|
||||||
array_push($query['attrs'],'+');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $base)
|
|
||||||
$bases = $server->getBaseDN();
|
|
||||||
else
|
|
||||||
$bases = array($base);
|
|
||||||
|
|
||||||
foreach ($bases as $base) {
|
|
||||||
$query['base'] = $base;
|
|
||||||
|
|
||||||
$time_start = utime();
|
|
||||||
$this->results[$base] = $server->query($query,null);
|
|
||||||
$time_end = utime();
|
|
||||||
|
|
||||||
usort($this->results[$base],'pla_compare_dns');
|
|
||||||
$this->resultsdata[$base]['time'] = round($time_end-$time_start,2);
|
|
||||||
|
|
||||||
# If no result, there is a something wrong
|
|
||||||
if (! $this->results[$base] && $server->getErrorNum(null))
|
|
||||||
system_message(array(
|
|
||||||
'title'=>_('Encountered an error while performing search.'),
|
|
||||||
'body'=>ldap_error_msg($server->getErrorMessage(null),$server->getErrorNum(null)),
|
|
||||||
'type'=>'error'));
|
|
||||||
|
|
||||||
$this->items += count($this->results[$base]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->resultsdata['scope'] = $query['scope'];
|
|
||||||
$this->resultsdata['filter'] = $query['filter'];
|
|
||||||
$this->resultsdata['attrs'] = $query['attrs'];
|
|
||||||
|
|
||||||
# Other settings
|
|
||||||
switch (get_request('format','POST',false,'unix')) {
|
|
||||||
case 'win':
|
|
||||||
$this->br = "\r\n";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'mac':
|
|
||||||
$this->br = "\r";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'unix':
|
|
||||||
default:
|
|
||||||
$this->br = "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get_request('compress','REQUEST') == 'on')
|
|
||||||
$this->compress = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isCompressed() {
|
|
||||||
return $this->compress;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getHeader() {
|
|
||||||
$server = $this->getServer();
|
|
||||||
$type = $this->getType();
|
|
||||||
|
|
||||||
$output = '';
|
|
||||||
|
|
||||||
$output .= sprintf('# %s %s %s%s',$type['description'],_('for'),implode('|',array_keys($this->results)),$this->br);
|
|
||||||
$output .= sprintf('# %s: %s (%s)%s',_('Server'),$server->getName(),$server->getValue('server','host'),$this->br);
|
|
||||||
$output .= sprintf('# %s: %s%s',_('Search Scope'),$this->resultsdata['scope'],$this->br);
|
|
||||||
$output .= sprintf('# %s: %s%s',_('Search Filter'),$this->resultsdata['filter'],$this->br);
|
|
||||||
$output .= sprintf('# %s: %s%s',_('Total Entries'),$this->items,$this->br);
|
|
||||||
$output .= sprintf('#%s',$this->br);
|
|
||||||
$output .= sprintf('# Generated by %s (%s) on %s%s',app_name(),get_href('web'),date('F j, Y g:i a'),$this->br);
|
|
||||||
$output .= sprintf('# Version: %s%s',app_version(),$this->br);
|
|
||||||
|
|
||||||
$output .= $this->br;
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to check if the attribute value should be base 64 encoded.
|
|
||||||
*
|
|
||||||
* @param The string to check.
|
|
||||||
* @return boolean true if the string is safe ascii, false otherwise.
|
|
||||||
*/
|
|
||||||
protected function isSafeAscii($str) {
|
|
||||||
for ($i=0;$i<strlen($str);$i++)
|
|
||||||
if (ord($str[$i]) < 32 || ord($str[$i]) > 127)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export entries to CSV
|
|
||||||
*
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @subpackage Export
|
|
||||||
*/
|
|
||||||
class ExportCSV extends Export {
|
|
||||||
private $separator = ',';
|
|
||||||
private $qualifier = '"';
|
|
||||||
private $multivalue_separator = ' | ';
|
|
||||||
private $escapeCode = '"';
|
|
||||||
|
|
||||||
static public function getType() {
|
|
||||||
return array('type'=>'CSV','description' => 'CSV (Spreadsheet)','extension'=>'csv');
|
|
||||||
}
|
|
||||||
|
|
||||||
function export() {
|
|
||||||
$server = $this->getServer();
|
|
||||||
|
|
||||||
/* Go thru and find all the attribute names first. This is needed, because, otherwise we have
|
|
||||||
* no idea as to which search attributes were actually populated with data */
|
|
||||||
$headers = array('dn');
|
|
||||||
$entries = array();
|
|
||||||
foreach ($this->results as $base => $results) {
|
|
||||||
foreach ($results as $dndetails) {
|
|
||||||
array_push($entries,$dndetails);
|
|
||||||
|
|
||||||
unset($dndetails['dn']);
|
|
||||||
foreach (array_keys($dndetails) as $key)
|
|
||||||
if (! in_array($key,$headers))
|
|
||||||
array_push($headers,$key);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$output = '';
|
|
||||||
$num_headers = count($headers);
|
|
||||||
|
|
||||||
# Print out the headers
|
|
||||||
for ($i=0; $i<$num_headers; $i++) {
|
|
||||||
$output .= sprintf('%s%s%s',$this->qualifier,$headers[$i],$this->qualifier);
|
|
||||||
|
|
||||||
if ($i < $num_headers-1)
|
|
||||||
$output .= $this->separator;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Drop out our DN header.
|
|
||||||
array_shift($headers);
|
|
||||||
$num_headers--;
|
|
||||||
|
|
||||||
$output .= $this->br;
|
|
||||||
|
|
||||||
# Loop on every entry
|
|
||||||
foreach ($entries as $index => $entry) {
|
|
||||||
$dn = $entry['dn'];
|
|
||||||
unset($entry['dn']);
|
|
||||||
$output .= sprintf('%s%s%s%s',$this->qualifier,$this->LdapEscape($dn),$this->qualifier,$this->separator);
|
|
||||||
|
|
||||||
# Print the attributes
|
|
||||||
for ($j=0; $j<$num_headers; $j++) {
|
|
||||||
$attr = $headers[$j];
|
|
||||||
$output .= $this->qualifier;
|
|
||||||
|
|
||||||
if (array_key_exists($attr,$entry)) {
|
|
||||||
$binary_attribute = $server->isAttrBinary($attr) ? 1 : 0;
|
|
||||||
|
|
||||||
if (! is_array($entry[$attr]))
|
|
||||||
$attr_values = array($entry[$attr]);
|
|
||||||
else
|
|
||||||
$attr_values = $entry[$attr];
|
|
||||||
|
|
||||||
$num_attr_values = count($attr_values);
|
|
||||||
|
|
||||||
for ($i=0; $i<$num_attr_values; $i++) {
|
|
||||||
if ($binary_attribute)
|
|
||||||
$output .= base64_encode($attr_values[$i]);
|
|
||||||
else
|
|
||||||
$output .= $this->LdapEscape($attr_values[$i]);
|
|
||||||
|
|
||||||
if ($i < $num_attr_values-1)
|
|
||||||
$output .= $this->multivalue_separator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= $this->qualifier;
|
|
||||||
|
|
||||||
if ($j < $num_headers-1)
|
|
||||||
$output .= $this->separator;
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= $this->br;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->compress)
|
|
||||||
return gzencode($output);
|
|
||||||
else
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to escape data, where the qualifier happens to also
|
|
||||||
* be in the data.
|
|
||||||
*/
|
|
||||||
private function LdapEscape ($var) {
|
|
||||||
return str_replace($this->qualifier,$this->escapeCode.$this->qualifier,$var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export entries to DSML v.1
|
|
||||||
*
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @subpackage Export
|
|
||||||
*/
|
|
||||||
class ExportDSML extends Export {
|
|
||||||
static public function getType() {
|
|
||||||
return array('type'=>'DSML','description' => _('DSML V.1 Export'),'extension'=>'xml');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export entries to DSML format
|
|
||||||
*/
|
|
||||||
function export() {
|
|
||||||
$server = $this->getServer();
|
|
||||||
|
|
||||||
# Not very elegant, but do the job for the moment as we have just 4 level
|
|
||||||
$indent = array();
|
|
||||||
$indent['dir'] = ' ';
|
|
||||||
$indent['ent'] = ' ';
|
|
||||||
$indent['att'] = ' ';
|
|
||||||
$indent['val'] = ' ';
|
|
||||||
|
|
||||||
# Print declaration
|
|
||||||
$output = sprintf('<?xml version="1.0"?>%s',$this->br);
|
|
||||||
|
|
||||||
# Print root element
|
|
||||||
$output .= sprintf('<dsml>%s',$this->br);
|
|
||||||
|
|
||||||
# Print info related to this export
|
|
||||||
$output .= sprintf('<!--%s',$this->br);
|
|
||||||
$output .= $this->getHeader();
|
|
||||||
$output .= sprintf('-->%s',$this->br);
|
|
||||||
$output .= $this->br;
|
|
||||||
|
|
||||||
$output .= sprintf('%s<directory-entries>%s',$indent['dir'],$this->br);
|
|
||||||
|
|
||||||
# Sift through the entries.
|
|
||||||
$counter = 0;
|
|
||||||
foreach ($this->results as $base => $results) {
|
|
||||||
foreach ($results as $dndetails) {
|
|
||||||
$counter++;
|
|
||||||
|
|
||||||
$dn = $dndetails['dn'];
|
|
||||||
unset($dndetails['dn']);
|
|
||||||
ksort($dndetails);
|
|
||||||
|
|
||||||
# Display DN
|
|
||||||
$output .= sprintf('%s<entry dn="%s">%s',$indent['ent'],htmlspecialchars($dn),$this->br);
|
|
||||||
|
|
||||||
# Display the objectClass attributes first
|
|
||||||
if (isset($dndetails['objectClass'])) {
|
|
||||||
if (! is_array($dndetails['objectClass']))
|
|
||||||
$dndetails['objectClass'] = array($dndetails['objectClass']);
|
|
||||||
|
|
||||||
$output .= sprintf('%s<objectClass>%s',$indent['att'],$this->br);
|
|
||||||
|
|
||||||
foreach ($dndetails['objectClass'] as $ocValue)
|
|
||||||
$output .= sprintf('%s<oc-value>%s</oc-value>%s',$indent['val'],$ocValue,$this->br);
|
|
||||||
|
|
||||||
$output .= sprintf('%s</objectClass>%s',$indent['att'],$this->br);
|
|
||||||
unset($dndetails['objectClass']);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Display the attributes
|
|
||||||
foreach ($dndetails as $key => $attr) {
|
|
||||||
if (! is_array($attr))
|
|
||||||
$attr = array($attr);
|
|
||||||
|
|
||||||
$output .= sprintf('%s<attr name="%s">%s',$indent['att'],$key,$this->br);
|
|
||||||
|
|
||||||
# If the attribute is binary, set the flag $binary_mode to true
|
|
||||||
$binary_mode = $server->isAttrBinary($key) ? 1 : 0;
|
|
||||||
|
|
||||||
foreach ($attr as $value)
|
|
||||||
$output .= sprintf('%s<value>%s</value>%s',
|
|
||||||
$indent['val'],($binary_mode ? base64_encode($value) : htmlspecialchars($value)),$this->br);
|
|
||||||
|
|
||||||
$output .= sprintf('%s</attr>%s',$indent['att'],$this->br);
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= sprintf('%s</entry>%s',$indent['ent'],$this->br);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= sprintf('%s</directory-entries>%s',$indent['dir'],$this->br);
|
|
||||||
$output .= sprintf('</dsml>%s',$this->br);
|
|
||||||
|
|
||||||
if ($this->compress)
|
|
||||||
return gzencode($output);
|
|
||||||
else
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export from LDAP using an LDIF format
|
|
||||||
*
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @subpackage Export
|
|
||||||
*/
|
|
||||||
class ExportLDIF extends Export {
|
|
||||||
# The maximum length of the ldif line
|
|
||||||
private $line_length = 76;
|
|
||||||
|
|
||||||
static public function getType() {
|
|
||||||
return array('type'=>'LDIF','description' => _('LDIF Export'),'extension'=>'ldif');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export entries to LDIF format
|
|
||||||
*/
|
|
||||||
public function export() {
|
|
||||||
if (! $this->results) {
|
|
||||||
echo _('Nothing to export');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$server = $this->getServer();
|
|
||||||
|
|
||||||
$output = $this->getHeader();
|
|
||||||
|
|
||||||
# Add our version.
|
|
||||||
$output .= 'version: 1';
|
|
||||||
$output .= $this->br;
|
|
||||||
$output .= $this->br;
|
|
||||||
|
|
||||||
# Sift through the entries.
|
|
||||||
$counter = 0;
|
|
||||||
foreach ($this->results as $base => $results) {
|
|
||||||
foreach ($results as $dndetails) {
|
|
||||||
$counter++;
|
|
||||||
|
|
||||||
$dn = $dndetails['dn'];
|
|
||||||
unset($dndetails['dn']);
|
|
||||||
ksort($dndetails);
|
|
||||||
|
|
||||||
$title_string = sprintf('# %s %s: %s%s',_('Entry'),$counter,$dn,$this->br);
|
|
||||||
|
|
||||||
if (strlen($title_string) > $this->line_length-3)
|
|
||||||
$title_string = substr($title_string,0,$this->line_length-3).'...'.$this->br;
|
|
||||||
|
|
||||||
$output .= $title_string;
|
|
||||||
|
|
||||||
# Display dn
|
|
||||||
if ($this->isSafeAscii($dn))
|
|
||||||
$output .= $this->multiLineDisplay(sprintf('dn: %s',$dn));
|
|
||||||
else
|
|
||||||
$output .= $this->multiLineDisplay(sprintf('dn:: %s',base64_encode($dn)));
|
|
||||||
|
|
||||||
# display the attributes
|
|
||||||
foreach ($dndetails as $key => $attr) {
|
|
||||||
if (! is_array($attr))
|
|
||||||
$attr = array($attr);
|
|
||||||
|
|
||||||
foreach ($attr as $value)
|
|
||||||
if (! $this->isSafeAscii($value) || $server->isAttrBinary($key))
|
|
||||||
$output .= $this->multiLineDisplay(sprintf('%s:: %s',$key,base64_encode($value)));
|
|
||||||
else
|
|
||||||
$output .= $this->multiLineDisplay(sprintf('%s: %s',$key,$value));
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= $this->br;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->compress)
|
|
||||||
return gzencode($output);
|
|
||||||
else
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to wrap ldif lines
|
|
||||||
*
|
|
||||||
* @param The line to be wrapped if needed.
|
|
||||||
*/
|
|
||||||
private function multiLineDisplay($str) {
|
|
||||||
$length_string = strlen($str);
|
|
||||||
$length_max = $this->line_length;
|
|
||||||
|
|
||||||
$output = '';
|
|
||||||
while ($length_string > $length_max) {
|
|
||||||
$output .= substr($str,0,$length_max).$this->br.' ';
|
|
||||||
$str = substr($str,$length_max,$length_string);
|
|
||||||
$length_string = strlen($str);
|
|
||||||
|
|
||||||
/* Need to do minus one to align on the right
|
|
||||||
* the first line with the possible following lines
|
|
||||||
* as these will have an extra space. */
|
|
||||||
$length_max = $this->line_length-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= $str.$this->br;
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export entries to VCARD v2.1
|
|
||||||
*
|
|
||||||
* @package phpLDAPadmin
|
|
||||||
* @subpackage Export
|
|
||||||
*/
|
|
||||||
class ExportVCARD extends Export {
|
|
||||||
static public function getType() {
|
|
||||||
return array('type'=>'VCARD','description' => _('VCARD 2.1 Export'),'extension'=>'vcf');
|
|
||||||
}
|
|
||||||
|
|
||||||
# Mappping one to one attribute
|
|
||||||
private $mapping = array(
|
|
||||||
'cn' => 'FN',
|
|
||||||
'title' => 'TITLE',
|
|
||||||
'homephone' => 'TEL;HOME',
|
|
||||||
'mobile' => 'TEL;CELL',
|
|
||||||
'mail' => 'EMAIL;Internet',
|
|
||||||
'labeleduri' =>'URL',
|
|
||||||
'o' => 'ORG',
|
|
||||||
'audio' => 'SOUND',
|
|
||||||
'facsmiletelephoneNumber' =>'TEL;WORK;HOME;VOICE;FAX',
|
|
||||||
'jpegphoto' => 'PHOTO;ENCODING=BASE64',
|
|
||||||
'businesscategory' => 'ROLE',
|
|
||||||
'description' => 'NOTE'
|
|
||||||
);
|
|
||||||
|
|
||||||
private $deliveryAddress = array(
|
|
||||||
'postofficebox',
|
|
||||||
'street',
|
|
||||||
'l',
|
|
||||||
'st',
|
|
||||||
'postalcode',
|
|
||||||
'c');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export entries to VCARD format
|
|
||||||
*/
|
|
||||||
function export() {
|
|
||||||
$server = $this->getServer();
|
|
||||||
$output = '';
|
|
||||||
|
|
||||||
# Sift through the entries.
|
|
||||||
foreach ($this->results as $base => $results) {
|
|
||||||
foreach ($results as $dndetails) {
|
|
||||||
$dndetails = array_change_key_case($dndetails);
|
|
||||||
|
|
||||||
# Check the attributes needed for the delivery address field
|
|
||||||
$addr = 'ADR:';
|
|
||||||
foreach ($this->deliveryAddress as $attr) {
|
|
||||||
if (isset($dndetails[$attr])) {
|
|
||||||
$addr .= $dndetails[$attr];
|
|
||||||
unset($dndetails[$attr]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$addr .= ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= sprintf('BEGIN:VCARD%s',$this->br);
|
|
||||||
|
|
||||||
# Loop for the attributes
|
|
||||||
foreach ($dndetails as $key => $attr) {
|
|
||||||
if (! is_array($attr))
|
|
||||||
$attr = array($attr);
|
|
||||||
|
|
||||||
# If an attribute of the ldap entry exist in the mapping array for vcard
|
|
||||||
if (isset($this->mapping[$key])) {
|
|
||||||
|
|
||||||
# Case of organisation. Need to append the possible ou attribute
|
|
||||||
if ($key == 'o') {
|
|
||||||
$output .= sprintf('%s:%s',$this->mapping[$key],$attr[0]);
|
|
||||||
|
|
||||||
if (isset($entry['ou']))
|
|
||||||
foreach ($entry['ou'] as $ou_value)
|
|
||||||
$output .= sprintf(';%s',$ou_value);
|
|
||||||
|
|
||||||
# The attribute is binary. (to do : need to fold the line)
|
|
||||||
} elseif (in_array($key,array('audio','jpegphoto'))) {
|
|
||||||
$output .= $this->mapping[$key].':'.$this->br;
|
|
||||||
$output .= ' '.base64_encode($attr[0]);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$output .= $this->mapping[$key].':'.$attr[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= $this->br;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= sprintf('UID:%s%s',isset($dndetails['entryUUID']) ? $dndetails['entryUUID'] : $dndetails['dn'],$this->br);
|
|
||||||
$output .= sprintf('VERSION:2.1%s',$this->br);
|
|
||||||
$output .= sprintf('%s%s',$addr,$this->br);
|
|
||||||
$output .= sprintf('END:VCARD%s',$this->br);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->compress)
|
|
||||||
return gzencode($output);
|
|
||||||
else
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
28
public/css/fixes.css
vendored
28
public/css/fixes.css
vendored
@ -241,3 +241,31 @@ p {
|
|||||||
.was-validated .form-control:invalid, .form-control.is-invalid {
|
.was-validated .form-control:invalid, .form-control.is-invalid {
|
||||||
border-color: #d92550 !important;
|
border-color: #d92550 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-monospace {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding:5px;
|
||||||
|
white-space: -moz-pre-wrap; /* Mozilla, supported since 1999 */
|
||||||
|
white-space: -pre-wrap; /* Opera */
|
||||||
|
white-space: -o-pre-wrap; /* Opera */
|
||||||
|
white-space: pre-wrap; /* CSS3 – Text module (Candidate Recommendation) http://www.w3.org/TR/css3-text/#white-space */
|
||||||
|
word-wrap: break-word; /* IE 5.5+ */
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
counter-reset: line-numbering;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code .line::before {
|
||||||
|
content: counter(line-numbering);
|
||||||
|
counter-increment: line-numbering;
|
||||||
|
padding-right: .8em; /* space after numbers */
|
||||||
|
margin-right: 1em;
|
||||||
|
width: 4em;
|
||||||
|
text-align: right;
|
||||||
|
opacity: 0.5;
|
||||||
|
display: inline-block;
|
||||||
|
border-right: 1px solid rgba(0, 0, 0, .5);
|
||||||
|
}
|
@ -35,6 +35,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@yield('page-modals')
|
||||||
|
|
||||||
@section('scripts')
|
@section('scripts')
|
||||||
@include('architect::layouts.partials.scripts')
|
@include('architect::layouts.partials.scripts')
|
||||||
|
|
||||||
|
@ -27,23 +27,31 @@
|
|||||||
|
|
||||||
<div tabindex="-1" role="menu" aria-hidden="true" class="dropdown-menu dropdown-menu-right">
|
<div tabindex="-1" role="menu" aria-hidden="true" class="dropdown-menu dropdown-menu-right">
|
||||||
<ul class="nav flex-column">
|
<ul class="nav flex-column">
|
||||||
@if ((isset($page_actions) && $page_actions->search('edit') !== FALSE) || old())
|
@if ((isset($page_actions) && ($page_actions->search('edit') !== FALSE)) || old())
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<span class="nav-link pt-0 pb-1">
|
<span class="nav-link pt-0 pb-1">
|
||||||
<button id="entry-edit" class="p-2 m-0 border-0 btn-transition btn btn-outline-dark w-100 text-start">
|
<button id="entry-edit" class="p-2 m-0 border-0 btn-transition btn btn-outline-dark w-100 text-start">
|
||||||
<i class="fas fa-fw fa-edit me-2"></i>
|
<i class="fas fa-fw fa-edit me-2"></i> @lang('Edit')
|
||||||
@lang('Edit')
|
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if (isset($page_actions) && $page_actions->search('copy') !== FALSE)
|
@if (isset($page_actions) && ($page_actions->search('export') !== FALSE))
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link pt-0 pb-1">
|
||||||
|
<button type="button" class="p-2 m-0 border-0 btn-transition btn btn-outline-dark w-100 text-start" data-bs-toggle="modal" data-bs-target="#entry-export-modal" {{--data-bs-whatever="ldif"--}}>
|
||||||
|
<i class="fas fa-fw fa-file-export me-2"></i> @lang('Export')
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (isset($page_actions) && ($page_actions->search('copy') !== FALSE))
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link pt-0 pb-1">
|
<a class="nav-link pt-0 pb-1">
|
||||||
<button class="p-2 m-0 border-0 btn-transition btn btn-outline-dark w-100 text-start">
|
<button class="p-2 m-0 border-0 btn-transition btn btn-outline-dark w-100 text-start">
|
||||||
<i class="fas fa-fw fa-truck-moving me-2"></i>
|
<i class="fas fa-fw fa-truck-moving me-2"></i> @lang('Copy or Move')
|
||||||
@lang('Copy or Move')
|
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -40,6 +40,9 @@
|
|||||||
<div class="font-icon-wrapper float-start ms-1 me-1 server-icon">
|
<div class="font-icon-wrapper float-start ms-1 me-1 server-icon">
|
||||||
<a class="p-0 m-0" href="{{ LaravelLocalization::localizeUrl('schema') }}" onclick="return false;" style="display: contents;"><i class="fas fa-fw fa-fingerprint"></i></a>
|
<a class="p-0 m-0" href="{{ LaravelLocalization::localizeUrl('schema') }}" onclick="return false;" style="display: contents;"><i class="fas fa-fw fa-fingerprint"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="font-icon-wrapper float-start ms-1 me-1 server-icon">
|
||||||
|
<a class="p-0 m-0" href="{{ LaravelLocalization::localizeUrl('import') }}" onclick="return false;" style="display: contents;"><i class="fas fa-fw fa-upload"></i></a>
|
||||||
|
</div>
|
||||||
@env(['local'])
|
@env(['local'])
|
||||||
<div class="font-icon-wrapper float-end ms-1 server-icon">
|
<div class="font-icon-wrapper float-end ms-1 server-icon">
|
||||||
<a class="p-0 m-0" href="{{ LaravelLocalization::localizeUrl('debug') }}" onclick="return false;" style="display: contents;"><i class="fas fa-fw fa-toolbox"></i></a>
|
<a class="p-0 m-0" href="{{ LaravelLocalization::localizeUrl('debug') }}" onclick="return false;" style="display: contents;"><i class="fas fa-fw fa-toolbox"></i></a>
|
||||||
|
9
resources/views/errors/555.blade.php
Normal file
9
resources/views/errors/555.blade.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@extends('architect::layouts.error')
|
||||||
|
|
||||||
|
@section('title')
|
||||||
|
@lang('Not Implemented') <small>(555)</small>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
{{ $exception->getMessage() }}
|
||||||
|
@endsection
|
9
resources/views/errors/598.blade.php
Normal file
9
resources/views/errors/598.blade.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@extends('architect::layouts.error')
|
||||||
|
|
||||||
|
@section('title')
|
||||||
|
@lang('Error') <small>(598)</small>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
{{ $exception->getMessage() }}
|
||||||
|
@endsection
|
3
resources/views/fragment/export.blade.php
Normal file
3
resources/views/fragment/export.blade.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<pre>
|
||||||
|
{{ $result }}
|
||||||
|
</pre>
|
@ -3,7 +3,7 @@
|
|||||||
@section('page_title')
|
@section('page_title')
|
||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="{{ ($x=Arr::get($o->getAttributes(),'jpegphoto')) ? 'border' : '' }}" rowspan="2">{!! $x ? $x->render() : sprintf('<div class="page-title-icon f32"><i class="%s"></i></div>',$o->icon() ?? "fas fa-info") !!}</td>
|
<td class="{{ ($x=$o->getObject('jpegphoto')) ? 'border' : '' }}" rowspan="2">{!! $x ? $x->render() : sprintf('<div class="page-title-icon f32"><i class="%s"></i></div>',$o->icon() ?? "fas fa-info") !!}</td>
|
||||||
<td class="text-end align-text-top p-0 {{ $x ? 'ps-5' : 'pt-2' }}"><strong>{{ $dn }}</strong></td>
|
<td class="text-end align-text-top p-0 {{ $x ? 'ps-5' : 'pt-2' }}"><strong>{{ $dn }}</strong></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -11,11 +11,11 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="p-1 m-1">Created</td>
|
<td class="p-1 m-1">Created</td>
|
||||||
<th class="p-1 m-1">{{ ($x=Arr::get($o->getAttributes(),'createtimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=Arr::get($o->getAttributes(),'creatorsname')) ? $x->render() : __('Unknown') }}]</th>
|
<th class="p-1 m-1">{{ ($x=$o->getObject('createtimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=$o->getObject('creatorsname')) ? $x->render() : __('Unknown') }}]</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="p-1 m-1">Modified</td>
|
<td class="p-1 m-1">Modified</td>
|
||||||
<th class="p-1 m-1">{{ ($x=Arr::get($o->getAttributes(),'modifytimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=Arr::get($o->getAttributes(),'modifiersname')) ? $x->render() : __('Unknown') }}]</th>
|
<th class="p-1 m-1">{{ ($x=$o->getObject('modifytimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=$o->getObject('modifiersname')) ? $x->render() : __('Unknown') }}]</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="p-1 m-1">UUID</td>
|
<td class="p-1 m-1">UUID</td>
|
||||||
@ -168,8 +168,43 @@
|
|||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
@section('page-modals')
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="entry-export-modal" tabindex="-1" aria-labelledby="entry-export-label" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-fullscreen-xl-down">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5" id="entry-export-label">LDIF for {{ $dn }}</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="entry-export"><div class="fa-3x"><i class="fas fa-spinner fa-pulse fa-sm"></i></div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button id="entry-export-download" type="button" class="btn btn-primary btn-sm">Download</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
@section('page-scripts')
|
@section('page-scripts')
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
function download(filename,text) {
|
||||||
|
var element = document.createElement('a');
|
||||||
|
|
||||||
|
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
|
||||||
|
element.setAttribute('download', filename);
|
||||||
|
element.style.display = 'none';
|
||||||
|
document.body.appendChild(element);
|
||||||
|
|
||||||
|
element.click();
|
||||||
|
document.body.removeChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
function editmode() {
|
function editmode() {
|
||||||
$('button[id=entry-edit]').addClass('active').removeClass('btn-outline-dark').addClass('btn-outline-light');
|
$('button[id=entry-edit]').addClass('active').removeClass('btn-outline-dark').addClass('btn-outline-light');
|
||||||
|
|
||||||
@ -190,17 +225,16 @@
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#form-reset').click(function() {
|
$('#form-reset').click(function() {
|
||||||
$('#dn-edit')[0].reset();
|
$('#dn-edit')[0].reset();
|
||||||
})
|
});
|
||||||
|
|
||||||
$('#form-submit').click(function() {
|
$('#form-submit').click(function() {
|
||||||
$('#dn-edit')[0].submit();
|
$('#dn-edit')[0].submit();
|
||||||
})
|
});
|
||||||
|
|
||||||
$('#newattr').on('change',function(item) {
|
$('#newattr').on('change',function(item) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
beforeSend: function() {
|
beforeSend: function() {},
|
||||||
},
|
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
$('#newattrs').append(data);
|
$('#newattrs').append(data);
|
||||||
},
|
},
|
||||||
@ -210,7 +244,7 @@
|
|||||||
},
|
},
|
||||||
url: '{{ url('entry/newattr') }}/'+item.target.value,
|
url: '{{ url('entry/newattr') }}/'+item.target.value,
|
||||||
cache: false
|
cache: false
|
||||||
})
|
});
|
||||||
|
|
||||||
// Remove the option from the list
|
// Remove the option from the list
|
||||||
$(this).find('[value="'+item.target.value+'"]').remove()
|
$(this).find('[value="'+item.target.value+'"]').remove()
|
||||||
@ -229,6 +263,29 @@
|
|||||||
editmode();
|
editmode();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#entry-export-download').on('click',function(item) {
|
||||||
|
item.preventDefault();
|
||||||
|
|
||||||
|
let ldif = $('#entry-export').find('pre:first'); // update this selector in your local version
|
||||||
|
download('ldap-export.ldif',ldif.html());
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#entry-export-modal').on('shown.bs.modal', function () {
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
beforeSend: function() {},
|
||||||
|
success: function(data) {
|
||||||
|
$('#entry-export').empty().append(data);
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
if (e.status != 412)
|
||||||
|
alert('That didnt work? Please try again....');
|
||||||
|
},
|
||||||
|
url: '{{ url('entry/export',$o->getDNSecure()) }}/',
|
||||||
|
cache: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
@if(old())
|
@if(old())
|
||||||
editmode();
|
editmode();
|
||||||
@endif
|
@endif
|
||||||
|
65
resources/views/frames/import.blade.php
Normal file
65
resources/views/frames/import.blade.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
@extends('layouts.dn')
|
||||||
|
|
||||||
|
@section('page_title')
|
||||||
|
<table class="table table-borderless">
|
||||||
|
<tr>
|
||||||
|
<td style="border-radius: 5px;"><div class="page-title-icon f32"><i class="fas fa-upload"></i></div></td>
|
||||||
|
<td class="top text-start align-text-top p-0 pt-2"><strong>@lang('LDIF Import')</strong><br><small>To Server XXX</small></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('main-content')
|
||||||
|
<div class="row">
|
||||||
|
<div class="offset-1 col-10">
|
||||||
|
<div class="main-card mb-3 card">
|
||||||
|
<form id="import-form" action="{{ url('import/process/ldif') }}" method="POST" enctype="multipart/form-data">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="frame" value="import">
|
||||||
|
|
||||||
|
<div class="card-header">
|
||||||
|
@lang('LDIF Import')
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="ldif-text" class="pb-2"><strong>@lang('Paste in your LDIF here')</strong></label>
|
||||||
|
<textarea class="form-control text-monospace @error('text') is-invalid @enderror" name="text" rows="10">{{ old('text') }}</textarea>
|
||||||
|
|
||||||
|
<div class="invalid-feedback pb-2">
|
||||||
|
@error('text')
|
||||||
|
{{ $message }}
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row pt-5">
|
||||||
|
<div class="col">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="ldif-file" class="pb-2"><strong>@lang('Or upload LDIF file')</strong></label><br>
|
||||||
|
<input type="file" class="form-control-file @error('file') is-invalid @enderror" name="file" accept=".ldif"><br>
|
||||||
|
<small class="form-text text-muted @error('file') is-invalid @enderror">@lang('Maximum file size') <strong>{{ ini_get('upload_max_filesize') }}</strong></small>
|
||||||
|
<div class="invalid-feedback pb-2">
|
||||||
|
@error('file')
|
||||||
|
{{ $message }}
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-footer">
|
||||||
|
<span class="ms-auto">
|
||||||
|
<button type="submit" class="btn btn-success btn-sm">Process</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
60
resources/views/frames/import_result.blade.php
Normal file
60
resources/views/frames/import_result.blade.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
@section('page_title')
|
||||||
|
<table class="table table-borderless">
|
||||||
|
<tr>
|
||||||
|
<td style="border-radius: 5px;"><div class="page-title-icon f32"><i class="fas fa-upload"></i></div></td>
|
||||||
|
<td class="top text-start align-text-top p-0 pt-2"><strong>@lang('LDIF Import Result')</strong><br><small>To Server XXX</small></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('main-content')
|
||||||
|
<div class="row">
|
||||||
|
<div class="offset col-12">
|
||||||
|
<div class="main-card mb-3 card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-header-tabs">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="nav-item"><a data-bs-toggle="tab" href="#result" class="nav-link active">@lang('Import Result')</a></li>
|
||||||
|
<li class="nav-item"><a data-bs-toggle="tab" href="#ldif" class="nav-link">@lang('LDIF')</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane active" id="result" role="tabpanel">
|
||||||
|
<table class="table table-borderless">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>@lang('DN')</th>
|
||||||
|
<th>@lang('Result')</th>
|
||||||
|
<th class="text-end">@lang('Line')</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
@foreach ($result as $item)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $item->get('dn') }}</td>
|
||||||
|
<td>{{ $item->get('result') }}</td>
|
||||||
|
<td class="text-end">{{ $item->get('line') }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane" id="ldif" role="tabpanel">
|
||||||
|
<pre class="code"><code>{{ $ldif }}</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('page-scripts')
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('pre code').html(function(index, html) {
|
||||||
|
return html.trim().replace(/^(.*)$/mg, "<span class=\"line\">$1</span>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@append
|
@ -4,26 +4,28 @@
|
|||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="border-radius: 5px;"><div class="page-title-icon f32"><i class="fas fa-info"></i></div></td>
|
<td style="border-radius: 5px;"><div class="page-title-icon f32"><i class="fas fa-info"></i></div></td>
|
||||||
<td class="top text-end align-text-top p-0 pt-2 }}"><strong>@lang('Server Info')</strong><br><small>{{ $s->rootDSE()->entryuuid[0] ?? '' }}</small></td>
|
<td class="top text-end align-text-top p-0 pt-2"><strong>@lang('Server Info')</strong><br><small>{{ $s->rootDSE()->entryuuid[0] ?? '' }}</small></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('main-content')
|
@section('main-content')
|
||||||
<div class="bg-white p-3">
|
<div class="main-card mb-3 card">
|
||||||
<table class="table">
|
<div class="card-body">
|
||||||
@foreach ($s->rootDSE()->getAttributes() as $attribute => $ao)
|
<table class="table">
|
||||||
<tr>
|
@foreach ($s->rootDSE()->getObjects() as $attribute => $ao)
|
||||||
<th class="w-25">
|
<tr>
|
||||||
{!! ($x=$s->schema('attributetypes',$attribute))
|
<th class="w-25">
|
||||||
? sprintf('<a class="attributetype" id="strtolower(%s)" href="%s">%s</a>',$x->name_lc,url('schema/attributetypes',$x->name_lc),$x->name)
|
{!! ($x=$s->schema('attributetypes',$attribute))
|
||||||
: $attribute !!}
|
? sprintf('<a class="attributetype" id="strtolower(%s)" href="%s">%s</a>',$x->name_lc,url('schema/attributetypes',$x->name_lc),$x->name)
|
||||||
</th>
|
: $attribute !!}
|
||||||
<td>
|
</th>
|
||||||
<x-attribute :edit="false" :o="$ao"/>
|
<td>
|
||||||
</td>
|
<x-attribute :edit="false" :o="$ao"/>
|
||||||
</tr>
|
</td>
|
||||||
@endforeach
|
</tr>
|
||||||
</table>
|
@endforeach
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
@ -10,4 +10,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@yield('page-modals')
|
||||||
@yield('page-scripts')
|
@yield('page-scripts')
|
@ -1,9 +1,11 @@
|
|||||||
@extends('dn')
|
@extends('home')
|
||||||
|
|
||||||
@section('page_title')
|
@section('page_title')
|
||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="{{ ($x=Arr::get($o->getOriginal(),'jpegphoto')) ? 'border' : '' }}" rowspan="2">{!! $x ? $x->render() : sprintf('<div class="page-title-icon f32"><i class="%s"></i></div>',$o->icon() ?? "fas fa-info") !!}</td>
|
<td class="{{ ($x=$o->getObject('jpegphoto')) ? 'border' : '' }}" rowspan="2">
|
||||||
|
{!! $x ? $x->render(FALSE,TRUE) : sprintf('<div class="page-title-icon f32"><i class="%s"></i></div>',$o->icon() ?? "fas fa-info") !!}
|
||||||
|
</td>
|
||||||
<td class="text-end align-text-top p-0 {{ $x ? 'ps-5' : 'pt-2' }}"><strong>{{ $dn }}</strong></td>
|
<td class="text-end align-text-top p-0 {{ $x ? 'ps-5' : 'pt-2' }}"><strong>{{ $dn }}</strong></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -11,11 +13,15 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="p-1 m-1">Created</td>
|
<td class="p-1 m-1">Created</td>
|
||||||
<th class="p-1 m-1">{{ ($x=Arr::get($o->getAttributes(),'createtimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=Arr::get($o->getAttributes(),'creatorsname')) ? $x->render() : __('Unknown') }}]</th>
|
<th class="p-1 m-1">
|
||||||
|
{{ ($x=$o->getObject('createtimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=$o->getObject('creatorsname')) ? $x->render() : __('Unknown') }}]
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="p-1 m-1">Modified</td>
|
<td class="p-1 m-1">Modified</td>
|
||||||
<th class="p-1 m-1">{{ ($x=Arr::get($o->getAttributes(),'modifytimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=Arr::get($o->getAttributes(),'modifiersname')) ? $x->render() : __('Unknown') }}]</th>
|
<th class="p-1 m-1">
|
||||||
|
{{ ($x=$o->getObject('modifytimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=$o->getObject('modifiersname')) ? $x->render() : __('Unknown') }}]
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="p-1 m-1">UUID</td>
|
<td class="p-1 m-1">UUID</td>
|
||||||
@ -62,13 +68,13 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="main-card mb-3 card">
|
<div class="main-card mb-3 card">
|
||||||
<div class="card-body">
|
<form id="dn-update" method="POST" class="needs-validation" action="{{ url('entry/update/commit') }}" novalidate>
|
||||||
<div class="row">
|
@csrf
|
||||||
<div class="col-12 col-lg-6 col-xl-4 mx-auto pt-3">
|
|
||||||
<form id="dn-edit" method="POST" class="needs-validation" action="{{ url('entry/update/commit') }}" novalidate>
|
|
||||||
@csrf
|
|
||||||
|
|
||||||
<input type="hidden" name="dn" value="{{ $o->getDNSecure() }}">
|
<input type="hidden" name="dn" value="{{ $o->getDNSecure() }}">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-lg-6 col-xl-4 mx-auto pt-3">
|
||||||
|
|
||||||
<div class="card-title"><h3>@lang('Do you want to make the following changes?')</h3></div>
|
<div class="card-title"><h3>@lang('Do you want to make the following changes?')</h3></div>
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
@ -96,7 +102,7 @@
|
|||||||
@endforeach
|
@endforeach
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row pt-3">
|
<div class="row pt-3">
|
||||||
@ -106,6 +112,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
@section('page-scripts')
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#form-reset').click(function() {
|
||||||
|
$('#dn-update')[0].reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#form-submit').click(function() {
|
||||||
|
$('#dn-update')[0].submit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@append
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
use App\Http\Controllers\HomeController;
|
use App\Http\Controllers\{HomeController,ImportController};
|
||||||
use App\Http\Controllers\Auth\LoginController;
|
use App\Http\Controllers\Auth\LoginController;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -30,6 +30,7 @@ Route::group(['prefix' => LaravelLocalization::setLocale()], function() {
|
|||||||
Route::get('info',[HomeController::class,'info']);
|
Route::get('info',[HomeController::class,'info']);
|
||||||
Route::post('dn',[HomeController::class,'dn_frame']);
|
Route::post('dn',[HomeController::class,'dn_frame']);
|
||||||
Route::get('debug',[HomeController::class,'debug']);
|
Route::get('debug',[HomeController::class,'debug']);
|
||||||
|
Route::get('import',[HomeController::class,'import_frame']);
|
||||||
Route::get('schema',[HomeController::class,'schema_frame']);
|
Route::get('schema',[HomeController::class,'schema_frame']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -42,3 +43,6 @@ Route::group(['prefix'=>'user'],function() {
|
|||||||
Route::post('entry/update/commit',[HomeController::class,'entry_update']);
|
Route::post('entry/update/commit',[HomeController::class,'entry_update']);
|
||||||
Route::post('entry/update/pending',[HomeController::class,'entry_pending_update']);
|
Route::post('entry/update/pending',[HomeController::class,'entry_pending_update']);
|
||||||
Route::get('entry/newattr/{id}',[HomeController::class,'entry_newattr']);
|
Route::get('entry/newattr/{id}',[HomeController::class,'entry_newattr']);
|
||||||
|
Route::get('entry/export/{id}',[HomeController::class,'entry_export']);
|
||||||
|
|
||||||
|
Route::post('import/process/{type}',[HomeController::class,'import']);
|
Loading…
Reference in New Issue
Block a user