Compare commits
8 Commits
ef4be7df64
...
26d1e12413
Author | SHA1 | Date | |
---|---|---|---|
26d1e12413 | |||
eeed0b04f6 | |||
7cf81c14d3 | |||
f5fe35c740 | |||
ff1f33385b | |||
b38edcccf9 | |||
86fad448b1 | |||
73f66eb282 |
@ -3,7 +3,7 @@ APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
|
||||
LOG_CHANNEL=stderr
|
||||
LOG_CHANNEL=single
|
||||
|
||||
CACHE_DRIVER=array
|
||||
QUEUE_CONNECTION=sync
|
||||
|
@ -39,57 +39,6 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
private const SYNTAX_CERTIFICATE = '1.3.6.1.4.1.1466.115.121.1.8';
|
||||
private const SYNTAX_CERTIFICATE_LIST = '1.3.6.1.4.1.1466.115.121.1.9';
|
||||
|
||||
/*
|
||||
# Has the attribute been modified
|
||||
protected $modified = false;
|
||||
# Is the attribute being deleted because of an object class removal
|
||||
protected $forcedelete = false;
|
||||
# Is the attribute visible
|
||||
protected $visible = false;
|
||||
protected $forcehide = false;
|
||||
# Is the attribute modifiable
|
||||
protected $readonly = false;
|
||||
# LDAP attribute type MUST/MAY
|
||||
protected $ldaptype = null;
|
||||
# Attribute property type (eg password, select, multiselect)
|
||||
protected $type = '';
|
||||
# Attribute value to keep unique
|
||||
protected $unique = false;
|
||||
|
||||
# Display parameters
|
||||
protected $display = '';
|
||||
protected $icon = '';
|
||||
protected $hint = '';
|
||||
# Helper details
|
||||
protected $helper = array();
|
||||
protected $helpervalue = array();
|
||||
# Onchange details
|
||||
protected $onchange = array();
|
||||
# Show spacer after this attribute is rendered
|
||||
protected $spacer = false;
|
||||
protected $verify = false;
|
||||
|
||||
# Component size
|
||||
protected $size = 0;
|
||||
# Value max length
|
||||
protected $maxlength = 0;
|
||||
# Text Area sizings
|
||||
protected $cols = 0;
|
||||
protected $rows = 0;
|
||||
|
||||
# Public for sorting
|
||||
public $page = 1;
|
||||
public $order = 255;
|
||||
public $ordersort = 255;
|
||||
|
||||
# Schema Aliases for this attribute (stored in lowercase)
|
||||
protected $aliases = array();
|
||||
|
||||
# Configuration for automatically generated values
|
||||
protected $autovalue = array();
|
||||
protected $postvalue = array();
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create an Attribute
|
||||
*
|
||||
@ -119,20 +68,6 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
$this->oc = $this->oc->merge($soc->getParents()->pluck('oid'));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
# Should this attribute be hidden
|
||||
if ($server->isAttrHidden($this->name))
|
||||
$this->forcehide = true;
|
||||
|
||||
# Should this attribute value be read only
|
||||
if ($server->isAttrReadOnly($this->name))
|
||||
$this->readonly = true;
|
||||
|
||||
# Should this attribute value be unique
|
||||
if ($server->isAttrUnique($this->name))
|
||||
$this->unique = true;
|
||||
*/
|
||||
}
|
||||
|
||||
public function __call(string $name,array $arguments)
|
||||
@ -255,6 +190,21 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
->get($tag,[]),$values)));
|
||||
}
|
||||
|
||||
/**
|
||||
* If this attribute has changes, re-render the attribute values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDirty(): array
|
||||
{
|
||||
$dirty = [];
|
||||
|
||||
if ($this->isDirty())
|
||||
$dirty = [$this->name_lc => $this->_values->toArray()];
|
||||
|
||||
return $dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hints about this attribute, ie: RDN, Required, etc
|
||||
*
|
||||
|
@ -56,6 +56,7 @@ abstract class Import
|
||||
{
|
||||
switch ($action) {
|
||||
case static::LDAP_IMPORT_ADD:
|
||||
case static::LDAP_IMPORT_MODIFY:
|
||||
try {
|
||||
$o->save();
|
||||
|
||||
@ -65,15 +66,17 @@ abstract class Import
|
||||
if ($e->getDetailedError())
|
||||
return collect([
|
||||
'dn'=>$o->getDN(),
|
||||
'result'=>sprintf('%d: %s (%s)',
|
||||
'link'=>$o->getDNSecure(),
|
||||
'result'=>sprintf('%d: %s%s',
|
||||
($x=$e->getDetailedError())->getErrorCode(),
|
||||
$x->getErrorMessage(),
|
||||
$x->getDiagnosticMessage(),
|
||||
$x->getDiagnosticMessage() ? ' ('.$x->getDiagnosticMessage().')' : '',
|
||||
)
|
||||
]);
|
||||
else
|
||||
return collect([
|
||||
'dn'=>$o->getDN(),
|
||||
'link'=>$o->getDNSecure(),
|
||||
'result'=>sprintf('%d: %s',
|
||||
$e->getCode(),
|
||||
$e->getMessage(),
|
||||
@ -81,9 +84,13 @@ abstract class Import
|
||||
]);
|
||||
}
|
||||
|
||||
Log::debug(sprintf('%s:Import Commited',self::LOGKEY));
|
||||
Log::debug(sprintf('%s:= Import Commited',self::LOGKEY));
|
||||
|
||||
return collect(['dn'=>$o->getDN(),'result'=>__('Created')]);
|
||||
return collect([
|
||||
'dn'=>$o->getDN(),
|
||||
'link'=>$o->getDNSecure(),
|
||||
'result'=>$action === self::LDAP_IMPORT_ADD ? __('Created') : __('Modified'),
|
||||
]);
|
||||
|
||||
default:
|
||||
throw new GeneralException('Unhandled action during commit: '.$action);
|
||||
|
@ -24,7 +24,9 @@ class LDIF extends Import
|
||||
public function process(): Collection
|
||||
{
|
||||
$c = 0;
|
||||
$action = NULL;
|
||||
$action = self::LDAP_IMPORT_ADD; // Assume add mode
|
||||
$subaction = 'add'; // Assume add
|
||||
$dn = NULL;
|
||||
$attribute = NULL;
|
||||
$base64encoded = FALSE;
|
||||
$o = NULL;
|
||||
@ -32,23 +34,38 @@ class LDIF extends Import
|
||||
$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));
|
||||
Log::debug(sprintf('%s:LDIF Line [%s] (%d)',self::LOGKEY,$line,$c));
|
||||
$line = trim($line);
|
||||
|
||||
// If the line starts with a comment, ignore it
|
||||
if (preg_match('/^#/',$line))
|
||||
continue;
|
||||
|
||||
// If the line starts with a dash, its more updates to the same entry
|
||||
if (preg_match('/^-/',$line)) {
|
||||
Log::debug(sprintf('%s:/ DASH Line [%s] (%d)',self::LOGKEY,$line,$c),['action'=>$action,'subaction'=>$subaction,'attribute'=>$attribute,'value'=>$value]);
|
||||
if ($attribute)
|
||||
$o = $this->entry($o,$attribute,$subaction,$base64encoded ? base64_decode($value) : $value,$c);
|
||||
|
||||
$base64encoded = FALSE;
|
||||
$attribute = NULL;
|
||||
$value = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have a blank line, then that completes this command
|
||||
if (! $line) {
|
||||
if (! is_null($o)) {
|
||||
// Add the last attribute;
|
||||
$o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||
// If we havent got a version yet, then we havent started
|
||||
if (! $version) {
|
||||
continue;
|
||||
|
||||
Log::debug(sprintf('%s:- Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||
} elseif (! is_null($o)) {
|
||||
if ($attribute)
|
||||
$o = $this->entry($o,$attribute,$subaction,$base64encoded ? base64_decode($value) : $value,$c);
|
||||
|
||||
Log::debug(sprintf('%s:- Committing Entry (More) [%s]',self::LOGKEY,$o->getDN()));
|
||||
|
||||
// Commit
|
||||
$result->push($this->commit($o,$action));
|
||||
@ -59,27 +76,123 @@ class LDIF extends Import
|
||||
$base64encoded = FALSE;
|
||||
$attribute = NULL;
|
||||
$value = '';
|
||||
|
||||
} else {
|
||||
throw new GeneralException(sprintf('Processing Error - Line exists [%s] on (%d) but object is NULL',$line,$c));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$m = [];
|
||||
preg_match('/^([a-zA-Z0-9;-]+)(:+)\s+(.*)$/',$line,$m);
|
||||
preg_match('/^([a-zA-Z0-9;-]+)(:+)\s*(.*)$/',$line,$m);
|
||||
|
||||
// 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] appending [%s] (%d)',self::LOGKEY,$attribute,$line,$c));
|
||||
|
||||
// add to last attr value
|
||||
continue;
|
||||
|
||||
} else {
|
||||
// If base64 mode was enabled, and there is a following attribute after a base64encoded attribute, it hasnt been processed yet
|
||||
if ($base64encoded) {
|
||||
Log::debug(sprintf('%s:- Completing base64 attribute [%s]',self::LOGKEY,$attribute));
|
||||
|
||||
$o = $this->entry($o,$attribute,$subaction,base64_decode($value),$c);
|
||||
|
||||
$attribute = NULL;
|
||||
}
|
||||
|
||||
$base64encoded = ($m[2] === '::');
|
||||
$value = $m[3];
|
||||
|
||||
// If we are base64encoded, we need to loop around
|
||||
if ($base64encoded) {
|
||||
$attribute = $m[1];
|
||||
Log::debug(sprintf('%s:/ Retrieving base64 attribute [%s] (%c)',self::LOGKEY,$attribute,$c));
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// changetype needs to be after the dn, and if it isnt we'll assume add.
|
||||
if ($dn && Arr::get($m,1) !== 'changetype') {
|
||||
if ($action === self::LDAP_IMPORT_ADD) {
|
||||
Log::debug(sprintf('%s:Creating new entry [%s]:',self::LOGKEY,$dn),['o'=>$o]);
|
||||
$o = new Entry;
|
||||
$o->setDn($dn);
|
||||
$dn = NULL;
|
||||
|
||||
} else {
|
||||
Log::debug(sprintf('%s:Looking for existing entry [%s]:',self::LOGKEY,$dn),['o'=>$o]);
|
||||
$o = Entry::find($dn);
|
||||
$dn = NULL;
|
||||
|
||||
if (! $o) {
|
||||
$result->push(collect(['dn'=>$dn,'result'=>__('Entry doesnt exist')]));
|
||||
$result->last()->put('line',$c);;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (Arr::get($m,1)) {
|
||||
case 'dn':
|
||||
$dn = $base64encoded ? base64_decode($value) : $value;
|
||||
Log::debug(sprintf('%s:# Got DN [%s]:',self::LOGKEY,$dn));
|
||||
|
||||
$value = '';
|
||||
$base64encoded = FALSE;
|
||||
break;
|
||||
|
||||
case 'changetype':
|
||||
if ($m[2] !== ':')
|
||||
throw new GeneralException(sprintf('ChangeType cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
|
||||
throw new GeneralException(sprintf('changetype cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
|
||||
|
||||
if (! is_null($o))
|
||||
throw new GeneralException(sprintf('Previous Entry not complete? (line %d)',$c));
|
||||
|
||||
Log::debug(sprintf('%s:- Action [%s]',self::LOGKEY,$m[3]));
|
||||
|
||||
switch ($m[3]) {
|
||||
// if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) {
|
||||
case 'add':
|
||||
$action = self::LDAP_IMPORT_ADD;
|
||||
break;
|
||||
|
||||
case 'modify':
|
||||
$action = self::LDAP_IMPORT_MODIFY;
|
||||
break;
|
||||
|
||||
/*
|
||||
case 'delete':
|
||||
$action = self::LDAP_IMPORT_DELETE;
|
||||
break;
|
||||
*/
|
||||
|
||||
// @todo modrdn|moddn
|
||||
default:
|
||||
throw new NotImplementedException(sprintf('Unknown change type [%s]? (line %d)',$m[3],$c));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
case 'replace':
|
||||
if ($action !== self::LDAP_IMPORT_MODIFY)
|
||||
throw new GeneralException(sprintf('%s action can only be used with changetype: modify (line %d)',$m[1],$c));
|
||||
|
||||
$subaction = $m[1];
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$subaction = $m[1];
|
||||
$attribute = $m[3];
|
||||
$value = NULL;
|
||||
break;
|
||||
|
||||
case 'version':
|
||||
if (! is_null($version))
|
||||
throw new VersionException(sprintf('Version has already been set at [%d]. (line %d)',$version,$c));
|
||||
@ -92,49 +205,14 @@ class LDIF extends Import
|
||||
|
||||
// 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;
|
||||
}
|
||||
// Start of a new attribute
|
||||
$attribute = $m[1];
|
||||
Log::debug(sprintf('%s:- Working with Attribute [%s] with [%s] (%d)',self::LOGKEY,$attribute,$value,$c));
|
||||
|
||||
// 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->addAttributeItem($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] === '::');
|
||||
$attribute = $m[1];
|
||||
$value = $m[3];
|
||||
|
||||
Log::debug(sprintf('%s:- New Attribute [%s] with [%s] (%d)',self::LOGKEY,$attribute,$value,$c));
|
||||
$o = $this->entry($o,$attribute,$subaction,$base64encoded ? base64_decode($value) : $value,$c);
|
||||
$attribute = NULL;
|
||||
$value = NULL;
|
||||
}
|
||||
|
||||
if ($version !== 1)
|
||||
@ -142,11 +220,11 @@ class LDIF extends Import
|
||||
}
|
||||
|
||||
// We may still have a pending action
|
||||
if ($action) {
|
||||
// Add the last attribute;
|
||||
$o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||
if ($o) {
|
||||
if ($attribute)
|
||||
$o = $this->entry($o,$attribute,$subaction,$base64encoded ? base64_decode($value) : $value,$c);
|
||||
|
||||
Log::debug(sprintf('%s:- Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||
Log::debug(sprintf('%s:- Committing Entry (Final) [%s]',self::LOGKEY,$o->getDN()));
|
||||
|
||||
// Commit
|
||||
$result->push($this->commit($o,$action));
|
||||
@ -156,75 +234,46 @@ class LDIF extends Import
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function xreadEntry() {
|
||||
static $haveVersion = FALSE;
|
||||
private function entry(Entry $o,string $attribute,string $subaction,?string $value,int $c): Entry
|
||||
{
|
||||
Log::debug(sprintf('%s:. %s Attribute [%s] value [%s] (%d)',self::LOGKEY,$subaction,$attribute,$value,$c));
|
||||
|
||||
if ($lines = $this->nextLines()) {
|
||||
switch ($subaction) {
|
||||
case 'add':
|
||||
if (! $value)
|
||||
throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c));
|
||||
|
||||
$server = $this->getServer();
|
||||
$o->addAttributeItem($attribute,$value);
|
||||
break;
|
||||
|
||||
# The first line should be the DN
|
||||
if (preg_match('/^dn:/',$lines[0])) {
|
||||
list($text,$dn) = $this->getAttrValue(array_shift($lines));
|
||||
case 'replace':
|
||||
if (! $value)
|
||||
throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c));
|
||||
|
||||
# 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);
|
||||
if (! ($x=$o->getObject(($xx=strstr($attribute,';',TRUE)) ?: $attribute)))
|
||||
throw new \Exception(sprintf('Attribute [%s] doesnt exist in [%s] (line %d)',$attribute,$o->getDn(),$c));
|
||||
|
||||
// If the attribute has changed, we'll assume this is an additional value for it
|
||||
if ($x->isDirty()) {
|
||||
Log::debug(sprintf('%s:/ Attribute [%s] has changed, assuming add',self::LOGKEY,$attribute));
|
||||
$o->addAttributeItem($attribute,$value);
|
||||
|
||||
} 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;
|
||||
$o->{$attribute} = [Entry::TAG_NOTAG=>[$value]];
|
||||
|
||||
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);
|
||||
case 'delete':
|
||||
if (! $o->getObject($attribute))
|
||||
throw new \Exception(sprintf('Attribute [%s] doesnt exist in [%s] (line %d)',$attribute,$o->getDn(),$c));
|
||||
|
||||
$o->{$attribute} = [];
|
||||
break;
|
||||
|
||||
default:
|
||||
if (! $server->dnExists($dn))
|
||||
return $this->error(_('Unknown change type'),$lines);
|
||||
throw new \Exception('Unknown subaction:'.$subaction);
|
||||
}
|
||||
|
||||
} else
|
||||
return $this->error(_('A valid dn line is required'),$lines);
|
||||
|
||||
} else
|
||||
return FALSE;
|
||||
return $o;
|
||||
}
|
||||
}
|
@ -181,8 +181,11 @@ final class Server
|
||||
$result = collect();
|
||||
|
||||
// @note: Incase our rootDSE didnt return a namingcontext, we'll have no base DNs
|
||||
foreach ($namingcontexts as $dn)
|
||||
$result->push(self::get($dn)->read()->find($dn));
|
||||
foreach ($namingcontexts as $dn) {
|
||||
$o = self::get($dn)->read()->find($dn);
|
||||
$o->setBase();
|
||||
$result->push($o);
|
||||
}
|
||||
|
||||
return $result->filter()->sort(fn($item)=>$item->sort_key);
|
||||
});
|
||||
@ -326,6 +329,11 @@ final class Server
|
||||
: NULL;
|
||||
}
|
||||
|
||||
public function hasMore(): bool
|
||||
{
|
||||
return (new Entry)->hasMore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this server support RFC3666 language tags
|
||||
* OID: 1.3.6.1.4.1.4203.1.5.4
|
||||
@ -501,10 +509,20 @@ final class Server
|
||||
* Get the Schema DN
|
||||
*
|
||||
* @return string
|
||||
* @throws ObjectNotFoundException
|
||||
*/
|
||||
public function schemaDN(): string
|
||||
{
|
||||
return Arr::get($this->rootDSE->subschemasubentry,0);
|
||||
}
|
||||
|
||||
public function subordinates(string $dn,array $attrs=['dn']): ?LDAPCollection
|
||||
{
|
||||
return $this
|
||||
->get(
|
||||
dn: $dn,
|
||||
attrs: array_merge($attrs,[]))
|
||||
->rawFilter('(hassubordinates=TRUE)')
|
||||
->search()
|
||||
->get() ?: NULL;
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ class AjaxController extends Controller
|
||||
{
|
||||
return Server::baseDNs()
|
||||
->map(fn($item)=> [
|
||||
'title'=>$item->getRdn(),
|
||||
'title'=>$item->is_base ? $item->getDn() : $item->getRdn(),
|
||||
'item'=>$item->getDNSecure(),
|
||||
'lazy'=>TRUE,
|
||||
'icon'=>'fa-fw fas fa-sitemap',
|
||||
@ -67,6 +67,17 @@ class AjaxController extends Controller
|
||||
]
|
||||
: []
|
||||
)
|
||||
->push(
|
||||
config('server')->hasMore()
|
||||
? [
|
||||
'title'=>sprintf('[%s]',__('Size Limit')),
|
||||
'item'=>'',
|
||||
'lazy'=>FALSE,
|
||||
'icon'=>'fas fa-fw fa-triangle-exclamation text-danger',
|
||||
'tooltip'=>__('There may be more entries'),
|
||||
]
|
||||
: []
|
||||
)
|
||||
->filter()
|
||||
->values();
|
||||
}
|
||||
@ -113,4 +124,27 @@ class AjaxController extends Controller
|
||||
'may' => $oc->getMayAttrs()->pluck('name'),
|
||||
];
|
||||
}
|
||||
|
||||
public function subordinates(?string $dn=NULL): array
|
||||
{
|
||||
$dn = $dn ? Crypt::decryptString($dn) : '';
|
||||
|
||||
// Sometimes our key has a command, so we'll ignore it
|
||||
if (str_starts_with($dn,'*') && ($x=strpos($dn,'|')))
|
||||
$dn = substr($dn,$x+1);
|
||||
|
||||
$result = collect();
|
||||
// If no DN, we'll find all children
|
||||
if (! $dn)
|
||||
foreach (Server::baseDNs() as $base)
|
||||
$result = $result->merge(config('server')
|
||||
->subordinates($base->getDN()));
|
||||
else
|
||||
$result = config('server')
|
||||
->subordinates(collect(explode(',',$dn))->last());
|
||||
|
||||
return
|
||||
$result->map(fn($item)=>['id'=>$item->getDNSecure(),'value'=>$item->getDN()])
|
||||
->toArray();
|
||||
}
|
||||
}
|
@ -112,6 +112,60 @@ class HomeController extends Controller
|
||||
->with('updated',FALSE);
|
||||
}
|
||||
|
||||
public function entry_copy_move(Request $request): \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
{
|
||||
$from_dn = Crypt::decryptString($request->post('dn'));
|
||||
Log::info(sprintf('%s:Renaming [%s] to [%s]',self::LOGKEY,$from_dn,$request->post('to_dn')));
|
||||
|
||||
$o = clone config('server')->fetch($from_dn);
|
||||
|
||||
if (! $o)
|
||||
return back()
|
||||
->withInput()
|
||||
->with('note',__('DN doesnt exist'));
|
||||
|
||||
$o->setDN($request->post('to_dn'));
|
||||
$o->exists = FALSE;
|
||||
|
||||
// Add the RDN attribute to match the new RDN
|
||||
$rdn = collect(explode(',',$request->post('to_dn')))->first();
|
||||
|
||||
list($attr,$value) = explode('=',$rdn);
|
||||
$o->{$attr} = [Entry::TAG_NOTAG => $o->getObject($attr)->tagValuesOld(Entry::TAG_NOTAG)->push($value)->unique()];
|
||||
|
||||
Log::info(sprintf('%s:Copying [%s] to [%s]',self::LOGKEY,$from_dn,$o->getDN()));
|
||||
|
||||
try {
|
||||
$o->save();
|
||||
|
||||
} catch (LdapRecordException $e) {
|
||||
return Redirect::to('/')
|
||||
->withInput(['_key'=>Crypt::encryptString('*dn|'.$from_dn)])
|
||||
->with('failed',sprintf('%s: %s - %s: %s',
|
||||
__('LDAP Server Error Code'),
|
||||
$e->getDetailedError()?->getErrorCode() ?: $e->getMessage(),
|
||||
$e->getDetailedError()?->getErrorMessage() ?: $e->getFile(),
|
||||
$e->getDetailedError()?->getDiagnosticMessage() ?: $e->getLine(),
|
||||
));
|
||||
}
|
||||
|
||||
if ($request->post('delete') && $request->post('delete') === '1') {
|
||||
Log::info(sprintf('%s:Deleting [%s] after copy',self::LOGKEY,$from_dn));
|
||||
|
||||
$x = $this->entry_delete($request);
|
||||
|
||||
return ($x->getSession()->has('success'))
|
||||
? Redirect::to('/')
|
||||
->withInput(['_key'=>Crypt::encryptString('*dn|'.$o->getDN())])
|
||||
->with('success',__('Entry copied and deleted'))
|
||||
: $x;
|
||||
}
|
||||
|
||||
return Redirect::to('/')
|
||||
->withInput(['_key'=>Crypt::encryptString('*dn|'.$o->getDN())])
|
||||
->with('success',__('Entry copied'));
|
||||
}
|
||||
|
||||
public function entry_create(EntryAddRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$key = $this->request_key($request,collect(old()));
|
||||
@ -154,7 +208,7 @@ class HomeController extends Controller
|
||||
->with('failed',sprintf('%s: %s - %s: %s',
|
||||
__('LDAP Server Error Code'),
|
||||
$e->getDetailedError()->getErrorCode(),
|
||||
__($e->getDetailedError()->getErrorMessage()),
|
||||
$e->getDetailedError()->getErrorMessage(),
|
||||
$e->getDetailedError()->getDiagnosticMessage(),
|
||||
));
|
||||
}
|
||||
@ -377,7 +431,7 @@ class HomeController extends Controller
|
||||
->with('failed',sprintf('%s: %s - %s: %s',
|
||||
__('LDAP Server Error Code'),
|
||||
$e->getDetailedError()->getErrorCode(),
|
||||
__($e->getDetailedError()->getErrorMessage()),
|
||||
$e->getDetailedError()->getErrorMessage(),
|
||||
$e->getDetailedError()->getDiagnosticMessage(),
|
||||
));
|
||||
}
|
||||
@ -435,8 +489,8 @@ class HomeController extends Controller
|
||||
->with('dn',$key['dn'])
|
||||
->with('o',$o)
|
||||
->with('page_actions',collect([
|
||||
'copy'=>FALSE,
|
||||
'create'=>($x=($o->getObjects()->except('entryuuid')->count() > 0)),
|
||||
'copy'=>$x,
|
||||
'delete'=>(! is_null($xx=$o->getObject('hassubordinates')->value)) && ($xx === 'FALSE'),
|
||||
'edit'=>$x,
|
||||
'export'=>$x,
|
||||
@ -484,9 +538,13 @@ class HomeController extends Controller
|
||||
$result = $import->process();
|
||||
|
||||
} catch (NotImplementedException $e) {
|
||||
Log::error(sprintf('Import Exception [%s]',$e->getMessage()));
|
||||
|
||||
abort(555,$e->getMessage());
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('Import Exception [%s]',$e->getMessage()));
|
||||
|
||||
abort(598,$e->getMessage());
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ class Entry extends Model
|
||||
|
||||
// For new entries, this is the container that this entry will be stored in
|
||||
private string $rdnbase;
|
||||
private(set) bool $is_base;
|
||||
|
||||
/* OVERRIDES */
|
||||
|
||||
@ -91,14 +92,39 @@ class Entry extends Model
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return $this->objects
|
||||
->filter(fn($item)=>(! $item->is_internal))
|
||||
->flatMap(fn($item)=>
|
||||
($item->no_attr_tags)
|
||||
$item->no_attr_tags
|
||||
? [strtolower($item->name)=>$item->values]
|
||||
: $item->values
|
||||
->flatMap(fn($v,$k)=>[strtolower($item->name.($k !== self::TAG_NOTAG ? ';'.$k : ''))=>$v]))
|
||||
->flatMap(fn($v,$k)=>[strtolower($item->name.(($k !== self::TAG_NOTAG) ? ';'.$k : ''))=>$v]))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* This replaces the model's get dirty, given that we store LDAP attributes in $this->objects, replacing
|
||||
* $this->original/$this->attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDirty(): array
|
||||
{
|
||||
$result = collect();
|
||||
|
||||
foreach ($this->objects as $o)
|
||||
if ($o->isDirty())
|
||||
$result = $result->merge($o->getDirty());
|
||||
|
||||
$result = $result
|
||||
->flatMap(function($item,$attr) {
|
||||
return ($x=collect($item))->count()
|
||||
? $x->flatMap(fn($v,$k)=>[strtolower($attr.(($k !== self::TAG_NOTAG) ? ';'.$k : ''))=>$v])
|
||||
: [strtolower($attr)=>[]];
|
||||
});
|
||||
|
||||
return $result->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the new and old values for a given key are equivalent.
|
||||
*/
|
||||
@ -452,7 +478,7 @@ class Entry extends Model
|
||||
$ot = $this->getOtherTags();
|
||||
|
||||
$cache[$tag ?: '_all_'] = $this->objects
|
||||
->filter(fn($item)=>(! $item->is_internal) && ((! $item->no_attr_tags) || (! $tag) || ($tag === Entry::TAG_NOTAG)))
|
||||
->filter(fn($item)=>(! $item->is_internal) && ((! $item->no_attr_tags) || (! $tag) || ($tag === self::TAG_NOTAG)))
|
||||
->filter(fn($item)=>(! $tag) || $ot->has($item->name_lc) || count($item->tagValues($tag)) > 0);
|
||||
}
|
||||
|
||||
@ -574,6 +600,16 @@ class Entry extends Model
|
||||
return [$attribute,$tags];
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this entry a baseDN
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setBase(): void
|
||||
{
|
||||
$this->is_base = TRUE;
|
||||
}
|
||||
|
||||
public function setRDNBase(string $bdn): void
|
||||
{
|
||||
if ($this->exists)
|
||||
|
@ -1 +1 @@
|
||||
v2.2.1-rel
|
||||
v2.2.2-dev
|
||||
|
@ -1,9 +1,18 @@
|
||||
/** ensure our userpassword has select is next to the password input */
|
||||
attribute#userpassword .select2-container--bootstrap-5 .select2-selection {
|
||||
font-size: inherit;
|
||||
/* ensure our password type select box shrinks and has the full boarder */
|
||||
attribute#userpassword .input-group > .form-select {
|
||||
flex-grow: 0;
|
||||
width: 9em;
|
||||
border: var(--bs-gray-500) 1px solid;
|
||||
background-color: #f0f0f0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
attribute#userpassword .input-group .select2-container--bootstrap-5 {
|
||||
flex-grow: 0;
|
||||
margin-bottom: 0.25em !important;
|
||||
}
|
||||
|
||||
attribute#userpassword .input-group > .select2-container--bootstrap-5 .select2-selection {
|
||||
background-color: #eef2f4;
|
||||
}
|
||||
|
||||
/* render the structural inside the input box */
|
||||
@ -14,20 +23,6 @@ attribute#objectclass .input-group-end:not(input.form-control) {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
/* select forms that have nothing next to them */
|
||||
.select-group:first-child .select2-container--bootstrap-5 .select2-selection {
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.input-group:first-child:not(.select-group) .select2-container--bootstrap-5 .select2-selection {
|
||||
border-bottom-right-radius: unset;
|
||||
border-top-right-radius: unset;
|
||||
}
|
||||
|
||||
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||
font-size: 0.88em;
|
||||
}
|
||||
|
||||
input.form-control.input-group-end {
|
||||
border-bottom-right-radius: 4px !important;
|
||||
border-top-right-radius: 4px !important;
|
||||
@ -98,3 +93,8 @@ input.form-control.input-group-end {
|
||||
ul.square {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
/* Fix select2 selections when set up with d-none */
|
||||
select[class*="d-none"] + span.select2 {
|
||||
display: none;
|
||||
}
|
@ -215,9 +215,17 @@ pre code .line::before {
|
||||
}
|
||||
|
||||
/** select2 rendering fixes */
|
||||
.select2-container--bootstrap-5 .select2-selection {
|
||||
border: var(--bs-gray-500) 1px solid;
|
||||
}
|
||||
|
||||
/* Selection rendered size */
|
||||
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||
font-size: 95%;
|
||||
}
|
||||
|
||||
/* The opened input box */
|
||||
.select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field {
|
||||
line-height: 1.0;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
|
||||
@ -226,23 +234,21 @@ pre code .line::before {
|
||||
font-size: 95%;
|
||||
}
|
||||
|
||||
select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear, .select2-container--bootstrap-5 .select2-selection--single .select2-selection__clear {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
}
|
||||
|
||||
/* Place holder text */
|
||||
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__rendered .select2-selection__placeholder {
|
||||
line-height: 1.0;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* Group options title rendering */
|
||||
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__group {
|
||||
color: #212529;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
/* Multiple selected items rendering */
|
||||
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice {
|
||||
padding: 0.25em 0.45em;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* Remove the shadow outline on an opened box */
|
||||
|
@ -89,7 +89,7 @@ $(document).ready(function() {
|
||||
types: 'active expanded focus selected' // which status types to store
|
||||
},
|
||||
click: function(event,data) {
|
||||
if (data.targetType === 'title')
|
||||
if (data.targetType === 'title' && data.node.data.item)
|
||||
getNode(data.node.data.item);
|
||||
},
|
||||
source: sources,
|
||||
|
@ -1,6 +1,5 @@
|
||||
<!-- $o=Attribute::class -->
|
||||
<x-attribute.layout :edit="$edit=($edit ?? FALSE)" :new="$new=($new ?? FALSE)" :o="$o" :template="$template">
|
||||
<div class="col-12">
|
||||
<div class="tab-content">
|
||||
@foreach($o->langtags as $langtag)
|
||||
<span @class(['tab-pane','active'=>$loop->index === 0]) id="langtag-{{ $o->name_lc }}-{{ $langtag }}" role="tabpanel">
|
||||
@ -22,7 +21,7 @@
|
||||
name="{{ $o->name_lc }}[{{ $langtag }}][]"
|
||||
value="{{ $value ?: ($av ?? '') }}"
|
||||
placeholder="{{ ! is_null($x=$tv->get($loop->index)) ? $x : '['.__('NEW').']' }}"
|
||||
@readonly(! $edit)
|
||||
@readonly(! $new)
|
||||
@disabled($o->isDynamic())>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@ -44,7 +43,6 @@
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-attribute.layout>
|
||||
|
||||
@if($new && ($x=$template?->onChange($o->name))?->count())
|
||||
|
@ -4,7 +4,7 @@
|
||||
@foreach(($o->tagValues($langtag)->count() ? $o->tagValues($langtag) : [$langtag => NULL]) as $key => $value)
|
||||
@if($edit)
|
||||
<div class="input-group has-validation">
|
||||
<x-form.select id="userpassword_hash_{{$loop->index}}{{$template?->name ?: ''}}" name="_userpassword_hash[{{ $langtag }}][]" :value="old('_userpassword_hash.'.$langtag.'.0',$o->hash($new ? '' : ($value ?? ''))->id())" :options="$helpers" allowclear="false" :disabled="! $new"/>
|
||||
<x-select class="mb-1" id="userpassword_hash_{{$loop->index}}{{$template?->name ?: ''}}" name="_userpassword_hash[{{ $langtag }}][]" :value="old('_userpassword_hash.'.$langtag.'.0',$o->hash($new ? '' : ($value ?? ''))->id())" :options="$helpers" allowclear="false" :disabled="! $new"/>
|
||||
<input type="password" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)),'mb-1','border-focus'=>! $o->tagValuesOld($langtag)->contains($value),'bg-success-subtle'=>$updated]) name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ Arr::get(old($o->name_lc),$langtag.'.'.$loop->index,$value ? md5($value) : '') }}" @readonly(! $new)>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
|
@ -3,7 +3,6 @@
|
||||
@foreach($o->langtags as $langtag)
|
||||
@foreach(($o->tagValues($langtag)->count() ? $o->tagValues($langtag) : [$langtag => NULL]) as $key => $value)
|
||||
@if($edit)
|
||||
<div class="select-group">
|
||||
<x-form.select
|
||||
@class(['is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)),'mb-1','border-focus'=>! $o->tagValuesOld($langtag)->contains($value)])
|
||||
id="{{ $o->name_lc }}_{{$loop->index}}{{$template?->name ?: ''}}"
|
||||
@ -19,7 +18,6 @@
|
||||
{{ join('|',$e) }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
{{ $o->render_item_old($langtag.'.'.$key) }}
|
||||
@endif
|
||||
|
@ -1,78 +1,7 @@
|
||||
<x-form.base {{ $attributes }}>
|
||||
@isset($name)
|
||||
@isset($name)
|
||||
<input type="hidden" id="{{ $id ?? $name }}_disabled" name="{{ $name }}" value="" disabled>
|
||||
@endisset
|
||||
@endisset
|
||||
|
||||
<select class="form-select @error($old ?? $id ?? $name) is-invalid @enderror" id="{{ $id ?? $name}}" @isset($name)name="{{ $name }}"@endisset @required($required ?? FALSE) @disabled($disabled ?? FALSE)>
|
||||
@if((empty($value) && ! empty($options)) || isset($addnew))
|
||||
<option value=""></option>
|
||||
|
||||
@isset($addnew)
|
||||
<option value="new">{{ $addnew ?: 'Add New' }}</option>
|
||||
@endisset
|
||||
@endif
|
||||
@isset($options)
|
||||
@empty($groupby)
|
||||
@foreach($options as $option)
|
||||
@continue(! Arr::get($option,'value'))
|
||||
<option value="{{ Arr::get($option,'id') }}" @selected(Arr::get($option,'id') == collect(old())->dot()->get(isset($old) ? $old.'.0' : ($id ?? $name),$value ?? ''))>{{ Arr::get($option,'value') }}</option>
|
||||
@endforeach
|
||||
|
||||
@else
|
||||
@foreach($options->groupBy($groupby) as $group)
|
||||
<optgroup label="{{ Arr::get($group->first(),$groupby) }}">
|
||||
@foreach($group as $option)
|
||||
@continue(! Arr::get($option,'value'))
|
||||
<option value="{{ Arr::get($option,'id') }}" @selected(Arr::get($option,'id') == collect(old())->dot()->get(isset($old) ? $old.'.0' : ($id ?? $name),$value ?? ''))>{{ Arr::get($option,'value') }}</option>
|
||||
@endforeach
|
||||
</optgroup>
|
||||
@endforeach
|
||||
@endempty
|
||||
@endisset
|
||||
</select>
|
||||
</x-form.base>
|
||||
|
||||
@section('page-scripts')
|
||||
<script type="text/javascript">
|
||||
// Select doesnt support read only so we'll use disable and a new field
|
||||
@isset($name)
|
||||
function {{$id ?? $name}}_readonly(on) {
|
||||
if (on) {
|
||||
$('#{{ $name }}').prop('disabled',true);
|
||||
$('#{{ $name }}_disabled').prop('disabled',false).val($('#{{ $name }}').val());
|
||||
|
||||
} else {
|
||||
$('#{{ $name }}').prop('disabled',false);
|
||||
$('#{{ $name }}_disabled').prop('disabled',true);
|
||||
}
|
||||
}
|
||||
@endisset
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#{{ $id ?? $name }}').select2({
|
||||
theme: 'bootstrap-5',
|
||||
dropdownAutoWidth: true,
|
||||
width: 'style',
|
||||
allowClear: {{ $allowclear ?? 'false' }},
|
||||
placeholder: '{{ $placeholder ?? '' }}',
|
||||
multiple: {{ $multiple ?? 'false' }},
|
||||
@isset($addvalues)
|
||||
tags: true,
|
||||
@endisset
|
||||
});
|
||||
|
||||
@if(isset($multiple) && (! $multiple))
|
||||
$('#{{ $id ?? $name }}').val(' ');
|
||||
$('#{{ $id ?? $name }}').trigger('change');
|
||||
@endif
|
||||
|
||||
@isset($options)
|
||||
@if(($autoselect ?? FALSE) && $options->count() === 1)
|
||||
$('#{{ $id ?? $name }}')
|
||||
.val('{{ $options->first()['id'] }}')
|
||||
.trigger("change")
|
||||
@endif
|
||||
@endisset
|
||||
});
|
||||
</script>
|
||||
@append
|
||||
<div class="input-group">
|
||||
<x-select :id="$id ?? NULL" :name="$name ?? NULL" :options="$options ?? []" :value="$value ?? NULL" :class="$class ?? NULL"/>
|
||||
</div>
|
72
resources/views/components/select.blade.php
Normal file
72
resources/views/components/select.blade.php
Normal file
@ -0,0 +1,72 @@
|
||||
<select class="form-select {{ $class }} @error($old ?? $id ?? $name) is-invalid @enderror" id="{{ $id ?? $name}}" @isset($name)name="{{ $name }}"@endisset @required($required ?? FALSE) @disabled($disabled ?? FALSE)>
|
||||
@if((empty($value) && ! empty($options)) || isset($addnew))
|
||||
<option value=""></option>
|
||||
|
||||
@isset($addnew)
|
||||
<option value="new">{{ $addnew ?: 'Add New' }}</option>
|
||||
@endisset
|
||||
@endif
|
||||
@isset($options)
|
||||
@empty($groupby)
|
||||
@foreach($options as $option)
|
||||
@continue(! Arr::get($option,'value'))
|
||||
<option value="{{ Arr::get($option,'id') }}" @selected(Arr::get($option,'id') == collect(old())->dot()->get(isset($old) ? $old.'.0' : ($id ?? $name),$value ?? ''))>{{ Arr::get($option,'value') }}</option>
|
||||
@endforeach
|
||||
|
||||
@else
|
||||
@foreach($options->groupBy($groupby) as $group)
|
||||
<optgroup label="{{ Arr::get($group->first(),$groupby) }}">
|
||||
@foreach($group as $option)
|
||||
@continue(! Arr::get($option,'value'))
|
||||
<option value="{{ Arr::get($option,'id') }}" @selected(Arr::get($option,'id') == collect(old())->dot()->get(isset($old) ? $old.'.0' : ($id ?? $name),$value ?? ''))>{{ Arr::get($option,'value') }}</option>
|
||||
@endforeach
|
||||
</optgroup>
|
||||
@endforeach
|
||||
@endempty
|
||||
@endisset
|
||||
</select>
|
||||
|
||||
@section('page-scripts')
|
||||
<script type="text/javascript">
|
||||
// Select doesnt support read only so we'll use disable and a new field
|
||||
@isset($name)
|
||||
function {{$id ?? $name}}_readonly(on) {
|
||||
if (on) {
|
||||
$('#{{ $name }}').prop('disabled',true);
|
||||
$('#{{ $name }}_disabled').prop('disabled',false).val($('#{{ $name }}').val());
|
||||
|
||||
} else {
|
||||
$('#{{ $name }}').prop('disabled',false);
|
||||
$('#{{ $name }}_disabled').prop('disabled',true);
|
||||
}
|
||||
}
|
||||
@endisset
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#{{ $id ?? $name }}').select2({
|
||||
theme: 'bootstrap-5',
|
||||
dropdownAutoWidth: false,
|
||||
width: 'style',
|
||||
allowClear: {{ $allowclear ?? 'false' }},
|
||||
placeholder: '{{ $placeholder ?? '' }}',
|
||||
multiple: {{ $multiple ?? 'false' }},
|
||||
@isset($addvalues)
|
||||
tags: true,
|
||||
@endisset
|
||||
});
|
||||
|
||||
@if(isset($multiple) && (! $multiple))
|
||||
$('#{{ $id ?? $name }}').val(' ');
|
||||
$('#{{ $id ?? $name }}').trigger('change');
|
||||
@endif
|
||||
|
||||
@isset($options)
|
||||
@if(($autoselect ?? FALSE) && $options->count() === 1)
|
||||
$('#{{ $id ?? $name }}')
|
||||
.val('{{ $options->first()['id'] }}')
|
||||
.trigger("change")
|
||||
@endif
|
||||
@endisset
|
||||
});
|
||||
</script>
|
||||
@append
|
@ -23,7 +23,9 @@
|
||||
@endif
|
||||
@if($page_actions->get('copy'))
|
||||
<li>
|
||||
<button class="btn btn-outline-dark p-1 m-1" id="entry-copy-move" data-bs-toggle="tooltip" data-bs-placement="bottom" title="@lang('Copy/Move')" disabled><i class="fas fa-fw fa-copy fs-5"></i></button>
|
||||
<span id="entry-copy-move" data-bs-toggle="modal" data-bs-target="#page-modal">
|
||||
<button class="btn btn-outline-dark p-1 m-1" data-bs-toggle="tooltip" data-bs-placement="bottom" title="@lang('Copy/Move')"><i class="fas fa-fw fa-copy fs-5"></i></button>
|
||||
</span>
|
||||
</li>
|
||||
@endif
|
||||
@if($page_actions->get('edit'))
|
||||
@ -79,7 +81,7 @@
|
||||
<span data-bs-toggle="tab" href="#template-{{ $template->name }}" @class(['btn','btn-outline-focus','active'=>$loop->index === 0])><i class="fa fa-fw pe-2 {{ $template->icon }}"></i> {{ $template->title }}</span>
|
||||
@endforeach
|
||||
@if($o->templates->count())
|
||||
<span data-bs-toggle="tab" href="#template-default" @class(['btn','btn-outline-focus','p-1','active'=>(! $o->templates->count())])>{{ __('LDAP Entry') }}</span>
|
||||
<span data-bs-toggle="tab" href="#template-default" @class(['btn','btn-outline-focus','p-1','active'=>(! $o->templates->count())])><i class="fa fa-fw fa-list pe-2"></i> {{ __('LDAP Entry') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@ -226,6 +228,25 @@
|
||||
var that = $(this).find('.modal-content');
|
||||
|
||||
switch ($(item.relatedTarget).attr('id')) {
|
||||
case 'entry-copy-move':
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
url: '{{ url('modal/copy-move') }}/'+dn,
|
||||
dataType: 'html',
|
||||
cache: false,
|
||||
beforeSend: function() {
|
||||
that.empty().append('<span class="p-3"><i class="fas fa-3x fa-spinner fa-pulse"></i></span>');
|
||||
},
|
||||
success: function(data) {
|
||||
that.empty().html(data);
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
case 'entry-delete':
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
|
@ -30,7 +30,13 @@
|
||||
</thead>
|
||||
@foreach($result as $item)
|
||||
<tr>
|
||||
<td>{{ $item->get('dn') }}</td>
|
||||
<td>
|
||||
@if($x=$item->get('link'))
|
||||
<a href="{{ url('/') }}#{{ $x }}">{{ $item->get('dn') }}</a>
|
||||
@else
|
||||
{{ $item->get('dn') }}
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $item->get('result') }}</td>
|
||||
<td class="text-end">{{ $item->get('line') }}</td>
|
||||
</tr>
|
||||
|
178
resources/views/modals/entry-copy-move.blade.php
Normal file
178
resources/views/modals/entry-copy-move.blade.php
Normal file
@ -0,0 +1,178 @@
|
||||
<div class="modal-header bg-dark text-white">
|
||||
<h1 class="modal-title fs-5">
|
||||
<i class="fas fa-fw fa-exclamation-triangle"></i> @lang('Rename') <strong>{{ $x=Crypt::decryptString($dn) }}</strong>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<span>
|
||||
@lang('New') DN: <strong><span id="newdn" class="fs-4 opacity-50"><small class="fs-5">[@lang('Select Base')]</small></span></strong>
|
||||
</span>
|
||||
<br>
|
||||
<br>
|
||||
<form id="entry-rename-form" method="POST" action="{{ url('entry/copy-move') }}">
|
||||
@csrf
|
||||
<input type="hidden" name="dn" value="{{ $dn }}">
|
||||
<input type="hidden" name="to_dn" value="">
|
||||
|
||||
<div class="row pb-3">
|
||||
<div class="col-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="delete-checkbox" name="delete" value="1">
|
||||
<label class="form-check-label" for="delete-checkbox">
|
||||
<i class="fas fa-fw fa-trash"></i> @lang('Delete after Copy')
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text" id="label">@lang('Select Base of Entry')</span>
|
||||
<input type="text" id="rdn" class="form-control d-none" style="width:20%;" placeholder="{{ $rdn=collect(explode(',',$x))->first() }}" value="{{ $rdn }}">
|
||||
<span class="input-group-text p-1 d-none">,</span>
|
||||
<select class="form-select w-25 d-none" id="rename-subbase" disabled style="width:30%;"></select>
|
||||
<span class="input-group-text p-1 d-none">,</span>
|
||||
<select class="form-select w-25" id="rename-base" style="width:30%;" disabled></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<x-modal.close/>
|
||||
<button id="entry-rename" type="button" class="btn btn-sm btn-primary">@lang('Copy')</button>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function refreshdn(value) {
|
||||
$('#newdn')
|
||||
.removeClass('opacity-50')
|
||||
.empty()
|
||||
.append(value);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
var rdn = '{{ $rdn }}';
|
||||
var base = '';
|
||||
|
||||
var that=$('#newdn');
|
||||
|
||||
// Get our bases
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: '{{ url('ajax/subordinates') }}',
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: function() {
|
||||
that.empty().append('<span class="p-3"><i class="fas fa-xs fa-spinner fa-pulse"></i></span>');
|
||||
},
|
||||
success: function(data) {
|
||||
that.empty().html('<small class="fs-5">[@lang('Select Base')]</small>');
|
||||
|
||||
$('#rename-base').children().remove();
|
||||
$('#rename-base').append('<option value=""></option>');
|
||||
data.forEach(function(item) {
|
||||
$('#rename-base').append('<option value="'+item.id+'">'+item.value+'</option>');
|
||||
});
|
||||
|
||||
$('#rename-base').prop('disabled',false);
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
});
|
||||
|
||||
// The base DN container
|
||||
$('#rename-base').select2({
|
||||
dropdownParent: $('#page-modal'),
|
||||
theme: 'bootstrap-5',
|
||||
dropdownAutoWidth: true,
|
||||
width: 'style',
|
||||
allowClear: false,
|
||||
placeholder: 'Choose Base',
|
||||
})
|
||||
.on('change',function() {
|
||||
$(this).prev().removeClass('d-none');
|
||||
$('#rename-subbase').removeClass('d-none')
|
||||
.prev().removeClass('d-none')
|
||||
.prev().removeClass('d-none');
|
||||
$('#label').empty().append("@lang('Complete Path')");
|
||||
|
||||
base = '';
|
||||
if (x=$('#rename-subbase option:selected').text())
|
||||
base += x+',';
|
||||
base += $('#rename-base option:selected').text();
|
||||
|
||||
refreshdn(rdn+','+base);
|
||||
var newdn = '';
|
||||
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url:'{{ url('ajax/children') }}',
|
||||
data: {_key: $(this).val() },
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: function() {
|
||||
newdn = that.text();
|
||||
console.log(newdn);
|
||||
that.empty().append('<span class="p-3"><i class="fas fa-xs fa-spinner fa-pulse"></i></span>');
|
||||
},
|
||||
success: function(data) {
|
||||
that.empty().text(newdn);
|
||||
$('#rename-subbase').children().remove();
|
||||
$('#rename-subbase').append('<option value=""></option>');
|
||||
data.forEach(function(item) {
|
||||
$('#rename-subbase').append('<option value="'+item.item+'">'+item.title+'</option>');
|
||||
});
|
||||
|
||||
$('#rename-subbase').prop('disabled',false);
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Optional make a child a new branch
|
||||
$('#rename-subbase').select2({
|
||||
dropdownParent: $('#page-modal'),
|
||||
theme: 'bootstrap-5',
|
||||
dropdownAutoWidth: true,
|
||||
width: 'style',
|
||||
allowClear: true,
|
||||
placeholder: 'New Subordinate (optional)',
|
||||
})
|
||||
.on('change',function(item) {
|
||||
base = '';
|
||||
if (x=$('#rename-subbase option:selected').text())
|
||||
base += x+',';
|
||||
base += $('#rename-base option:selected').text();
|
||||
|
||||
refreshdn(rdn+','+base);
|
||||
});
|
||||
|
||||
// Complete the RDN
|
||||
$('#rdn').on('input',function(item) {
|
||||
rdn = $(this).val();
|
||||
refreshdn(rdn+','+base);
|
||||
|
||||
$('button[id=entry-rename]').attr('disabled',! rdn.includes('='));
|
||||
})
|
||||
|
||||
// The submit button text
|
||||
$('input#delete-checkbox').on('change',function() {
|
||||
$('button#entry-rename').html($(this).prop('checked') ? '{{ __('Move') }}' : '{{ __('Copy') }}');
|
||||
});
|
||||
|
||||
// Submit
|
||||
$('button[id=entry-rename]').on('click',function() {
|
||||
$('input[name=to_dn]').val(rdn+','+base);
|
||||
$('form#entry-rename-form').submit();
|
||||
});
|
||||
});
|
||||
</script>
|
@ -43,18 +43,21 @@ Route::controller(HomeController::class)->group(function() {
|
||||
});
|
||||
|
||||
Route::match(['get','post'],'entry/add','entry_add');
|
||||
Route::post('entry/attr/add/{id}','entry_attr_add');
|
||||
Route::post('entry/create','entry_create');
|
||||
Route::post('entry/copy-move','entry_copy_move');
|
||||
Route::post('entry/delete','entry_delete');
|
||||
Route::get('entry/export/{id}','entry_export');
|
||||
Route::post('entry/password/check/','entry_password_check');
|
||||
Route::post('entry/attr/add/{id}','entry_attr_add');
|
||||
Route::post('entry/objectclass/add','entry_objectclass_add');
|
||||
Route::post('entry/rename','entry_rename');
|
||||
|
||||
Route::post('entry/update/commit','entry_update');
|
||||
Route::post('entry/update/pending','entry_pending_update');
|
||||
|
||||
Route::post('import/process/{type}','import');
|
||||
|
||||
Route::view('modal/copy-move/{dn}','modals.entry-copy-move');
|
||||
Route::view('modal/delete/{dn}','modals.entry-delete');
|
||||
Route::view('modal/export/{dn}','modals.entry-export');
|
||||
Route::view('modal/rename/{dn}','modals.entry-rename');
|
||||
@ -69,4 +72,5 @@ Route::controller(AjaxController::class)
|
||||
Route::post('children','children');
|
||||
Route::post('schema/view','schema_view');
|
||||
Route::post('schema/objectclass/attrs/{id}','schema_objectclass_attrs');
|
||||
Route::post('subordinates','subordinates');
|
||||
});
|
@ -7,8 +7,11 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Tests\TestCase;
|
||||
|
||||
use App\Ldap\Entry;
|
||||
|
||||
class ImportTest extends TestCase
|
||||
{
|
||||
// Test delete and create an entry
|
||||
public function testLDIF_Import()
|
||||
{
|
||||
$dn = 'cn=Bart Simpson,ou=People,o=Simpsons';
|
||||
@ -27,7 +30,7 @@ class ImportTest extends TestCase
|
||||
$x->delete();
|
||||
$this->assertEquals(NULL,config('server')->fetch($dn));
|
||||
|
||||
$file = new UploadedFile($import_file,'ldif-import.ldif',null,null,true);
|
||||
$file = new UploadedFile($import_file,basename($import_file),null,null,true);
|
||||
|
||||
$response = $this
|
||||
->actingAs(Auth::user())
|
||||
@ -44,5 +47,256 @@ class ImportTest extends TestCase
|
||||
// Check that it hsa been created
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
$this->assertCount(4,$x->getObject('objectclass'));
|
||||
$this->assertCount(4,$x->getObject('objectClass'));
|
||||
$this->assertCount(0,array_diff(['inetOrgPerson','posixAccount','top','shadowAccount'],$x->getObject('objectClass')->values->toArray()));
|
||||
$this->assertCount(1,$x->getObject('mail'));
|
||||
$this->assertContains(Entry::TAG_NOTAG.'.0',$x->getObject('mail')->values->dot()->keys());
|
||||
$this->assertContains('bart.simpson@example.com',$x->getObject('mail')->values->dot());
|
||||
$this->assertEquals(3024,strlen($x->getObject('jpegphoto')->values->dot()->first()));
|
||||
}
|
||||
|
||||
public function testLDIF_Import_Replace() {
|
||||
$dn = 'cn=Bart Simpson,ou=People,o=Simpsons';
|
||||
$import_file = __DIR__.'/data/ldif-import.1.ldif';
|
||||
|
||||
$this->assertTrue($this->login());
|
||||
|
||||
// Check that it exists
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
|
||||
$file = new UploadedFile($import_file,basename($import_file),null,null,true);
|
||||
|
||||
$response = $this
|
||||
->actingAs(Auth::user())
|
||||
->from('/import')
|
||||
->post('/import/process/ldif',[
|
||||
'_token' => csrf_token(),
|
||||
'_key'=>Crypt::encryptString('*import|_NOP'),
|
||||
'file' => $file,
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
|
||||
// Check that it hsa been created
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
$this->assertCount(4,$x->getObject('objectclass'));
|
||||
|
||||
$this->assertCount(1,$x->getObject('mail')->values);
|
||||
$this->assertCount(2,$x->getObject('mail')->tagValues());
|
||||
$this->assertCount(0,array_diff(['barts@email.com','secondmail@example.com'],$x->getObject('mail')->values->dot()->values()->toArray()));
|
||||
|
||||
$this->assertCount(1,$x->getObject('facsimiletelephonenumber')->values);
|
||||
$this->assertCount(1,$x->getObject('facsimiletelephonenumber')->tagValues());
|
||||
$this->assertCount(0,array_diff(['+1 555 222 4444'],$x->getObject('facsimiletelephonenumber')->values->dot()->values()->toArray()));
|
||||
}
|
||||
|
||||
public function testLDIF_Import_Delete() {
|
||||
$dn = 'cn=Bart Simpson,ou=People,o=Simpsons';
|
||||
$import_file = __DIR__.'/data/ldif-import.2.ldif';
|
||||
|
||||
$this->assertTrue($this->login());
|
||||
|
||||
// Check that it exists
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
|
||||
$file = new UploadedFile($import_file,basename($import_file),null,null,true);
|
||||
|
||||
$response = $this
|
||||
->actingAs(Auth::user())
|
||||
->from('/import')
|
||||
->post('/import/process/ldif',[
|
||||
'_token' => csrf_token(),
|
||||
'_key'=>Crypt::encryptString('*import|_NOP'),
|
||||
'file' => $file,
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
|
||||
// Check that it hsa been created
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
$this->assertCount(4,$x->getObject('objectclass'));
|
||||
|
||||
$this->assertCount(1,$x->getObject('mail')->values);
|
||||
$this->assertCount(2,$x->getObject('mail')->tagValues());
|
||||
$this->assertCount(0,array_diff(['barts@email.com','secondmail@example.com'],$x->getObject('mail')->values->dot()->values()->toArray()));
|
||||
|
||||
$this->assertNull($x->getObject('facsimiletelephonenumber'));
|
||||
}
|
||||
|
||||
public function testLDIF_Import_Append_Langtag() {
|
||||
$dn = 'cn=Bart Simpson,ou=People,o=Simpsons';
|
||||
$import_file = __DIR__.'/data/ldif-import.3.ldif';
|
||||
|
||||
$this->assertTrue($this->login());
|
||||
|
||||
// Check that it exists
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
|
||||
$file = new UploadedFile($import_file,basename($import_file),null,null,true);
|
||||
|
||||
$response = $this
|
||||
->actingAs(Auth::user())
|
||||
->from('/import')
|
||||
->post('/import/process/ldif',[
|
||||
'_token' => csrf_token(),
|
||||
'_key'=>Crypt::encryptString('*import|_NOP'),
|
||||
'file' => $file,
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
|
||||
// Check that it hsa been created
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
$this->assertCount(4,$x->getObject('objectclass'));
|
||||
|
||||
$this->assertCount(3,$x->getObject('mail')->values);
|
||||
$this->assertCount(4,$x->getObject('mail')->values->dot());
|
||||
$this->assertCount(2,$x->getObject('mail')->tagValues());
|
||||
$this->assertCount(1,$x->getObject('mail')->tagValues('lang-au'));
|
||||
$this->assertCount(1,$x->getObject('mail')->tagValues('lang-cn'));
|
||||
$this->assertCount(0,array_diff(['barts@email.com','secondmail@example.com','au-email@example.com','cn-email@example.com'],$x->getObject('mail')->values->dot()->values()->toArray()));
|
||||
}
|
||||
|
||||
public function testLDIF_Import_Replace_Langtag() {
|
||||
$dn = 'cn=Bart Simpson,ou=People,o=Simpsons';
|
||||
$import_file = __DIR__.'/data/ldif-import.4.ldif';
|
||||
|
||||
$this->assertTrue($this->login());
|
||||
|
||||
// Check that it exists
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
|
||||
$file = new UploadedFile($import_file,basename($import_file),null,null,true);
|
||||
|
||||
$response = $this
|
||||
->actingAs(Auth::user())
|
||||
->from('/import')
|
||||
->post('/import/process/ldif',[
|
||||
'_token' => csrf_token(),
|
||||
'_key'=>Crypt::encryptString('*import|_NOP'),
|
||||
'file' => $file,
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
|
||||
// Check that it hsa been created
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
$this->assertCount(4,$x->getObject('objectclass'));
|
||||
|
||||
$this->assertCount(3,$x->getObject('mail')->values);
|
||||
$this->assertCount(4,$x->getObject('mail')->values->dot());
|
||||
$this->assertCount(2,$x->getObject('mail')->tagValues());
|
||||
$this->assertCount(1,$x->getObject('mail')->tagValues('lang-au'));
|
||||
$this->assertCount(1,$x->getObject('mail')->tagValues('lang-cn'));
|
||||
$this->assertCount(0,array_diff(['notag@example.com','notag1@example.com','au-tag@example.com','cn-tag@example.com'],$x->getObject('mail')->values->dot()->values()->toArray()));
|
||||
}
|
||||
|
||||
public function testLDIF_Import_Add_Base64()
|
||||
{
|
||||
$dn = 'cn=Bart Simpson,ou=People,o=Simpsons';
|
||||
$import_file = __DIR__.'/data/ldif-import.5.ldif';
|
||||
|
||||
$this->assertTrue($this->login());
|
||||
|
||||
// Check that it exists
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
|
||||
$file = new UploadedFile($import_file,basename($import_file),null,null,true);
|
||||
|
||||
$response = $this
|
||||
->actingAs(Auth::user())
|
||||
->from('/import')
|
||||
->post('/import/process/ldif',[
|
||||
'_token' => csrf_token(),
|
||||
'_key'=>Crypt::encryptString('*import|_NOP'),
|
||||
'file' => $file,
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
|
||||
// Check that it hsa been created
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
$this->assertCount(4,$x->getObject('objectclass'));
|
||||
|
||||
$this->assertEquals(3396,strlen($x->getObject('jpegphoto')->values->dot()->first()));
|
||||
}
|
||||
|
||||
public function testLDIF_Import_Replace_Base64()
|
||||
{
|
||||
$dn = 'cn=Bart Simpson,ou=People,o=Simpsons';
|
||||
$import_file = __DIR__.'/data/ldif-import.6.ldif';
|
||||
|
||||
$this->assertTrue($this->login());
|
||||
|
||||
// Check that it exists
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
|
||||
$file = new UploadedFile($import_file,basename($import_file),null,null,true);
|
||||
|
||||
$response = $this
|
||||
->actingAs(Auth::user())
|
||||
->from('/import')
|
||||
->post('/import/process/ldif',[
|
||||
'_token' => csrf_token(),
|
||||
'_key'=>Crypt::encryptString('*import|_NOP'),
|
||||
'file' => $file,
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
|
||||
// Check that it hsa been created
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
$this->assertCount(4,$x->getObject('objectclass'));
|
||||
|
||||
$this->assertEquals(3024,strlen($x->getObject('jpegphoto')->values->dot()->first()));
|
||||
}
|
||||
|
||||
public function testLDIF_Import_Multi() {
|
||||
$dn = 'cn=Bart Simpson,ou=People,o=Simpsons';
|
||||
$import_file = __DIR__.'/data/ldif-import.7.ldif';
|
||||
|
||||
$this->assertTrue($this->login());
|
||||
|
||||
// Check that it exists
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
|
||||
$file = new UploadedFile($import_file,basename($import_file),null,null,true);
|
||||
|
||||
$response = $this
|
||||
->actingAs(Auth::user())
|
||||
->from('/import')
|
||||
->post('/import/process/ldif',[
|
||||
'_token' => csrf_token(),
|
||||
'_key'=>Crypt::encryptString('*import|_NOP'),
|
||||
'file' => $file,
|
||||
]);
|
||||
|
||||
$response->assertSuccessful();
|
||||
|
||||
// Check that it hsa been created
|
||||
$this->assertEquals($dn,$x=config('server')->fetch($dn));
|
||||
$this->assertTrue($x->exists);
|
||||
$this->assertCount(4,$x->getObject('objectclass'));
|
||||
|
||||
$this->assertCount(3,$x->getObject('mail')->values);
|
||||
$this->assertCount(6,$x->getObject('mail')->values->dot());
|
||||
$this->assertCount(2,$x->getObject('mail')->tagValues());
|
||||
$this->assertCount(2,$x->getObject('mail')->tagValues('lang-au'));
|
||||
$this->assertCount(2,$x->getObject('mail')->tagValues('lang-cn'));
|
||||
$this->assertCount(2,array_diff(['notag1@simpsons.example.com','notag2@simpsons.example.com','au-tag@simpsons.example.com','cn-tag@simpsons.example.com'],$x->getObject('mail')->values->dot()->values()->toArray()));
|
||||
}
|
||||
}
|
12
tests/Feature/data/ldif-import.1.ldif
Normal file
12
tests/Feature/data/ldif-import.1.ldif
Normal file
@ -0,0 +1,12 @@
|
||||
# Multiple actions
|
||||
version: 1
|
||||
dn: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
changetype: modify
|
||||
replace: mail
|
||||
mail: barts@email.com
|
||||
-
|
||||
add: mail
|
||||
mail: secondmail@example.com
|
||||
-
|
||||
add: facsimiletelephonenumber
|
||||
facsimiletelephonenumber: +1 555 222 4444
|
5
tests/Feature/data/ldif-import.2.ldif
Normal file
5
tests/Feature/data/ldif-import.2.ldif
Normal file
@ -0,0 +1,5 @@
|
||||
# Delete actions
|
||||
version: 1
|
||||
dn: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
changetype:modify
|
||||
delete: facsimiletelephonenumber
|
7
tests/Feature/data/ldif-import.3.ldif
Normal file
7
tests/Feature/data/ldif-import.3.ldif
Normal file
@ -0,0 +1,7 @@
|
||||
# Add lang-tag option with multiple values
|
||||
version: 1
|
||||
dn: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
changetype: modify
|
||||
add: mail
|
||||
mail;lang-au: au-email@example.com
|
||||
mail;lang-cn: cn-email@example.com
|
9
tests/Feature/data/ldif-import.4.ldif
Normal file
9
tests/Feature/data/ldif-import.4.ldif
Normal file
@ -0,0 +1,9 @@
|
||||
# Multiple values with tags and replace
|
||||
version: 1
|
||||
dn: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
changetype: modify
|
||||
replace: mail
|
||||
mail: notag@example.com
|
||||
mail: notag1@example.com
|
||||
mail;lang-au: au-tag@example.com
|
||||
mail;lang-cn: cn-tag@example.com
|
68
tests/Feature/data/ldif-import.5.ldif
Normal file
68
tests/Feature/data/ldif-import.5.ldif
Normal file
@ -0,0 +1,68 @@
|
||||
version: 1
|
||||
# Entry 1: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
dn: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
changetype:modify
|
||||
delete:jpegphoto
|
||||
-
|
||||
add:jpegphoto
|
||||
jpegPhoto:: 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
|
||||
zt1Zj1J/wDtsTcHQxCJtll8Rr3105kOg4DkF//Z
|
61
tests/Feature/data/ldif-import.6.ldif
Normal file
61
tests/Feature/data/ldif-import.6.ldif
Normal file
@ -0,0 +1,61 @@
|
||||
version: 1
|
||||
# Entry 1: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
dn: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
changetype:modify
|
||||
delete:jpegphoto
|
||||
-
|
||||
replace:jpegphoto
|
||||
jpegPhoto:: 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
|
12
tests/Feature/data/ldif-import.7.ldif
Normal file
12
tests/Feature/data/ldif-import.7.ldif
Normal file
@ -0,0 +1,12 @@
|
||||
version: 1
|
||||
dn: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
changetype: modify
|
||||
delete:mail
|
||||
|
||||
dn: cn=Bart Simpson,ou=People,o=Simpsons
|
||||
changetype: modify
|
||||
add: mail
|
||||
mail: notag1@simpsons.example.com
|
||||
mail: notag2@simpsons.example.com
|
||||
mail;lang-au: au-email@simpsons.example.com
|
||||
mail;lang-cn: cn-email@simpsons.example.com
|
Loading…
x
Reference in New Issue
Block a user