Compare commits
63 Commits
2.0.1
...
a7be4e00b4
Author | SHA1 | Date | |
---|---|---|---|
a7be4e00b4 | |||
2abc321eca | |||
6b2fb8dee4 | |||
66537dcec8 | |||
1bf8830887 | |||
c4d28c8a23 | |||
29c460fd4b | |||
3196b10aed | |||
f41b484dc4 | |||
855d7ae75c | |||
ffa8cdc826 | |||
8f39603f9f | |||
bcea6de791 | |||
28f4869628 | |||
cf535286c5 | |||
633513d3e9 | |||
705bfb2d64 | |||
3a3bf2addb | |||
5bb573100b | |||
a57ee78492 | |||
eab4f0427c | |||
fd2c5d1286 | |||
b35b44b2b8 | |||
ce66dcb2b5 | |||
56a91f853c | |||
81e0e58650 | |||
1470170928 | |||
85c7132b30 | |||
7e050954c3 | |||
16880cd0e2 | |||
696d87d190 | |||
87bae89ea3 | |||
1abc2cc6e1 | |||
1abab9db94 | |||
410daf649e | |||
9666841c3c | |||
9b33a20cc4 | |||
649749f9c1 | |||
5d3b8609bb | |||
93640959db | |||
f667250b2c | |||
4a84c25ac7 | |||
8ab5b4f35c | |||
de2d139288 | |||
d326d3c308 | |||
d3fc9c135f | |||
eb6e0b8d43 | |||
b01f7d5baf | |||
1ddb58ebbb | |||
b260912e01 | |||
7debd9ff2b | |||
49fd9b419a | |||
3161fe4fcb | |||
add3f85812 | |||
853bd92340 | |||
a56b2d8002 | |||
af7ca851d5 | |||
b34dad8836 | |||
ef2ea5e266 | |||
91b5b53137 | |||
d4c916923d | |||
e94a7d58e1 | |||
15d5bf605a |
@@ -3,7 +3,7 @@ run-name: ${{ gitea.actor }} Building Docker Image 🐳
|
||||
on: [push]
|
||||
env:
|
||||
DOCKER_HOST: tcp://127.0.0.1:2375
|
||||
ASSETS: 509b1a1
|
||||
ASSETS: c2780a3
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
@@ -46,6 +46,7 @@ The update to v2 is progressing well - here is a list of work to do and done:
|
||||
- [X] Delete extra values for Attributes that support multiple values
|
||||
- [ ] Delete Attributes
|
||||
- [ ] Templates to enable entries to conform to a custom standard
|
||||
- [ ] Autopopulate attribute values
|
||||
- [X] Login to LDAP server
|
||||
- [X] Configure login by a specific attribute
|
||||
- [X] Logout LDAP server
|
||||
@@ -53,12 +54,14 @@ The update to v2 is progressing well - here is a list of work to do and done:
|
||||
- [X] Import LDIF
|
||||
- [X] Schema Browser
|
||||
- [ ] Searching
|
||||
- [ ] Enforcing attribute uniqueness
|
||||
- [ ] Is there something missing?
|
||||
|
||||
Support is known for these LDAP servers:
|
||||
- [X] OpenLDAP
|
||||
- [X] OpenDJ
|
||||
- [ ] Microsoft Active Directory
|
||||
- [ ] 389 Directory Server
|
||||
|
||||
If there is an LDAP server that you have that you would like to have supported, please open an issue to request it.
|
||||
You might need to provide access, provide a copy or instructions to get an environment for testing. If you have enabled
|
||||
|
@@ -7,41 +7,35 @@ use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Classes\LDAP\Schema\AttributeType;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
/**
|
||||
* Represents an attribute of an LDAP Object
|
||||
*/
|
||||
class Attribute implements \Countable, \ArrayAccess, \Iterator
|
||||
class Attribute implements \Countable, \ArrayAccess
|
||||
{
|
||||
// Attribute Name
|
||||
protected string $name;
|
||||
private int $counter = 0;
|
||||
|
||||
protected ?AttributeType $schema = NULL;
|
||||
|
||||
/*
|
||||
# Source of this attribute definition
|
||||
protected $source;
|
||||
*/
|
||||
|
||||
// Current and Old Values
|
||||
protected Collection $values;
|
||||
|
||||
// Is this attribute an internal attribute
|
||||
protected bool $is_internal = FALSE;
|
||||
|
||||
// Is this attribute the RDN?
|
||||
protected bool $is_rdn = FALSE;
|
||||
protected(set) bool $is_internal = FALSE;
|
||||
protected(set) bool $no_attr_tags = FALSE;
|
||||
|
||||
// MIN/MAX number of values
|
||||
protected int $min_values_count = 0;
|
||||
protected int $max_values_count = 0;
|
||||
protected(set) int $min_values_count = 0;
|
||||
protected(set) int $max_values_count = 0;
|
||||
|
||||
// RFC3866 Language Tags
|
||||
protected Collection $lang_tags;
|
||||
// The schema's representation of this attribute
|
||||
protected(set) ?AttributeType $schema;
|
||||
|
||||
// The DN this object is in
|
||||
protected(set) string $dn;
|
||||
// The old values for this attribute - helps with isDirty() to determine if there is an update pending
|
||||
protected Collection $oldValues;
|
||||
private Collection $_values_old;
|
||||
// Current Values
|
||||
private Collection $_values;
|
||||
// The objectclasses of the entry that has this attribute
|
||||
protected(set) Collection $oc;
|
||||
|
||||
/*
|
||||
# Has the attribute been modified
|
||||
@@ -94,12 +88,22 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
||||
protected $postvalue = array();
|
||||
*/
|
||||
|
||||
public function __construct(string $name,array $values)
|
||||
/**
|
||||
* Create an Attribute
|
||||
*
|
||||
* @param string $dn DN this attribute is used in
|
||||
* @param string $name Name of the attribute
|
||||
* @param array $values Current Values
|
||||
* @param array $oc The objectclasses that the DN of this attribute has
|
||||
*/
|
||||
public function __construct(string $dn,string $name,array $values,array $oc=[])
|
||||
{
|
||||
$this->dn = $dn;
|
||||
$this->name = $name;
|
||||
$this->values = collect($values);
|
||||
$this->lang_tags = collect();
|
||||
$this->oldValues = collect($values);
|
||||
$this->_values = collect($values);
|
||||
$this->_values_old = collect($values);
|
||||
|
||||
$this->oc = collect($oc);
|
||||
|
||||
$this->schema = (new Server)
|
||||
->schema('attributetypes',$name);
|
||||
@@ -132,22 +136,22 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
||||
'hints' => $this->hints(),
|
||||
// Can this attribute be edited
|
||||
'is_editable' => $this->schema ? $this->schema->{$key} : NULL,
|
||||
// Is this an internal attribute
|
||||
'is_internal' => isset($this->{$key}) && $this->{$key},
|
||||
// Is this attribute the RDN
|
||||
'is_rdn' => $this->is_rdn,
|
||||
// Objectclasses that required this attribute for an LDAP entry
|
||||
'required' => $this->required(),
|
||||
// Is this attribute an RDN attribute
|
||||
'is_rdn' => $this->isRDN(),
|
||||
// We prefer the name as per the schema if it exists
|
||||
'name' => $this->schema ? $this->schema->{$key} : $this->{$key},
|
||||
// Attribute name in lower case
|
||||
'name_lc' => strtolower($this->name),
|
||||
// Old Values
|
||||
'old_values' => $this->oldValues,
|
||||
// Attribute values
|
||||
'values' => $this->values,
|
||||
// Required by Object Classes
|
||||
'required_by' => $this->schema?->required_by_object_classes ?: collect(),
|
||||
// Used in Object Classes
|
||||
'used_in' => $this->schema?->used_in_object_classes ?: collect(),
|
||||
// The current attribute values
|
||||
'values' => $this->no_attr_tags ? $this->tagValues() : $this->_values,
|
||||
// The original attribute values
|
||||
'values_old' => $this->no_attr_tags ? $this->tagValuesOld() : $this->_values_old,
|
||||
|
||||
default => throw new \Exception('Unknown key:' . $key),
|
||||
};
|
||||
@@ -156,11 +160,16 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
||||
public function __set(string $key,mixed $values): void
|
||||
{
|
||||
switch ($key) {
|
||||
case 'value':
|
||||
$this->values = collect($values);
|
||||
case 'values':
|
||||
$this->_values = $values;
|
||||
break;
|
||||
|
||||
case 'values_old':
|
||||
$this->_values_old = $values;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \Exception('Unknown key:'.$key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,49 +178,21 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function addValue(string $value): void
|
||||
{
|
||||
$this->values->push($value);
|
||||
}
|
||||
|
||||
public function current(): mixed
|
||||
{
|
||||
return $this->values->get($this->counter);
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
$this->counter++;
|
||||
}
|
||||
|
||||
public function key(): mixed
|
||||
{
|
||||
return $this->counter;
|
||||
}
|
||||
|
||||
public function valid(): bool
|
||||
{
|
||||
return $this->values->has($this->counter);
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->counter = 0;
|
||||
}
|
||||
/* INTERFACE */
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return $this->values->count();
|
||||
return $this->_values->dot()->count();
|
||||
}
|
||||
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
return ! is_null($this->values->has($offset));
|
||||
return $this->_values->dot()->has($offset);
|
||||
}
|
||||
|
||||
public function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
return $this->values->get($offset);
|
||||
return $this->_values->dot()->get($offset);
|
||||
}
|
||||
|
||||
public function offsetSet(mixed $offset, mixed $value): void
|
||||
@@ -224,15 +205,36 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
||||
// We cannot clear values using array syntax
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
public function addValue(string $tag,array $values): void
|
||||
{
|
||||
$this->_values->put(
|
||||
$tag,
|
||||
array_unique(array_merge($this->_values
|
||||
->get($tag,[]),$values)));
|
||||
}
|
||||
|
||||
public function addValueOld(string $tag,array $values): void
|
||||
{
|
||||
$this->_values_old->put(
|
||||
$tag,
|
||||
array_unique(array_merge($this->_values_old
|
||||
->get($tag,[]),$values)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hints about this attribute, ie: RDN, Required, etc
|
||||
*
|
||||
* @return array
|
||||
* @return Collection
|
||||
*/
|
||||
public function hints(): array
|
||||
public function hints(): Collection
|
||||
{
|
||||
$result = collect();
|
||||
|
||||
if ($this->is_internal)
|
||||
return $result;
|
||||
|
||||
// Is this Attribute an RDN
|
||||
if ($this->is_rdn)
|
||||
$result->put(__('rdn'),__('This attribute is required for the RDN'));
|
||||
@@ -240,17 +242,14 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
||||
// If this attribute name is an alias for the schema attribute name
|
||||
// @todo
|
||||
|
||||
// objectClasses requiring this attribute
|
||||
// @todo limit this to this DNs objectclasses
|
||||
// eg: $result->put('required','Required by objectClasses: a,b');
|
||||
if ($this->required_by->count())
|
||||
$result->put(__('required'),sprintf('%s: %s',__('Required Attribute by ObjectClass(es)'),$this->required_by->join(',')));
|
||||
if ($this->required()->count())
|
||||
$result->put(__('required'),sprintf('%s: %s',__('Required Attribute by ObjectClass(es)'),$this->required()->join(', ')));
|
||||
|
||||
// This attribute has language tags
|
||||
if ($this->lang_tags->count())
|
||||
$result->put(__('language tags'),sprintf('%s: %d',__('This Attribute has Language Tags'),$this->lang_tags->count()));
|
||||
// If this attribute is a dynamic attribute
|
||||
if ($this->isDynamic())
|
||||
$result->put(__('dynamic'),__('These are dynamic values present as a result of another attribute'));
|
||||
|
||||
return $result->toArray();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,13 +259,38 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
||||
*/
|
||||
public function isDirty(): bool
|
||||
{
|
||||
return ($this->oldValues->count() !== $this->values->count())
|
||||
|| ($this->values->diff($this->oldValues)->count() !== 0);
|
||||
return (($a=$this->values_old->dot()->filter())->keys()->count() !== ($b=$this->values->dot()->filter())->keys()->count())
|
||||
|| ($a->count() !== $b->count())
|
||||
|| ($a->diff($b)->count() !== 0);
|
||||
}
|
||||
|
||||
public function oldValues(array $array): void
|
||||
/**
|
||||
* Are these values as a result of a dynamic attribute
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDynamic(): bool
|
||||
{
|
||||
$this->oldValues = collect($array);
|
||||
return $this->schema->used_in_object_classes
|
||||
->keys()
|
||||
->intersect($this->schema->heirachy($this->oc))
|
||||
->count() === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Work out if this attribute is an RDN attribute
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRDN(): bool
|
||||
{
|
||||
// If we dont have an DN, then we cant know
|
||||
if (! $this->dn)
|
||||
return FALSE;
|
||||
|
||||
$rdns = collect(explode('+',substr($this->dn,0,strpos($this->dn,','))));
|
||||
|
||||
return $rdns->filter(fn($item) => str_starts_with($item,$this->name.'='))->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,37 +303,51 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
||||
*/
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||
{
|
||||
return view('components.attribute')
|
||||
$view = view()->exists($x='components.attribute.'.$this->name_lc)
|
||||
? view($x)
|
||||
: view('components.attribute');
|
||||
|
||||
return $view
|
||||
->with('o',$this)
|
||||
->with('edit',$edit)
|
||||
->with('old',$old)
|
||||
->with('new',$new);
|
||||
}
|
||||
|
||||
public function render_item_old(int $key): ?string
|
||||
public function render_item_old(string $dotkey): ?string
|
||||
{
|
||||
return Arr::get($this->old_values,$key);
|
||||
return Arr::get($this->values_old->dot(),$dotkey);
|
||||
}
|
||||
|
||||
public function render_item_new(int $key): ?string
|
||||
public function render_item_new(string $dotkey): ?string
|
||||
{
|
||||
return Arr::get($this->values,$key);
|
||||
return Arr::get($this->values->dot(),$dotkey);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this attribute has RFC3866 Language Tags, this will enable those values to be captured
|
||||
* Work out if this attribute is required by an objectClass the entry has
|
||||
*
|
||||
* @param string $tag
|
||||
* @param array $value
|
||||
* @return void
|
||||
* @return Collection
|
||||
*/
|
||||
public function setLangTag(string $tag,array $value): void
|
||||
public function required(): Collection
|
||||
{
|
||||
$this->lang_tags->put($tag,$value);
|
||||
// If we dont have any objectclasses then we cant know if it is required
|
||||
return $this->oc->count()
|
||||
? $this->oc->intersect($this->required_by->keys())->sort()
|
||||
: collect();
|
||||
}
|
||||
|
||||
public function setRDN(): void
|
||||
public function tagValues(string $tag=Entry::TAG_NOTAG): Collection
|
||||
{
|
||||
$this->is_rdn = TRUE;
|
||||
return collect($this->_values
|
||||
->filter(fn($item,$key)=>($key===$tag))
|
||||
->get($tag,[]));
|
||||
}
|
||||
|
||||
public function tagValuesOld(string $tag=Entry::TAG_NOTAG): Collection
|
||||
{
|
||||
return collect($this->_values_old
|
||||
->filter(fn($item,$key)=>($key===$tag))
|
||||
->get($tag,[]));
|
||||
}
|
||||
}
|
@@ -7,6 +7,6 @@ use App\Classes\LDAP\Attribute;
|
||||
/**
|
||||
* Represents an attribute whose values are binary
|
||||
*/
|
||||
class Binary extends Attribute
|
||||
abstract class Binary extends Attribute
|
||||
{
|
||||
}
|
@@ -5,6 +5,7 @@ namespace App\Classes\LDAP\Attribute\Binary;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Binary;
|
||||
use App\Ldap\Entry;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
/**
|
||||
@@ -14,13 +15,14 @@ final class JpegPhoto extends Binary
|
||||
{
|
||||
use MD5Updates;
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,string $langtag=Entry::TAG_NOTAG): View
|
||||
{
|
||||
return view('components.attribute.binary.jpegphoto')
|
||||
->with('o',$this)
|
||||
->with('edit',$edit)
|
||||
->with('old',$old)
|
||||
->with('new',$new)
|
||||
->with('langtag',$langtag)
|
||||
->with('f',new \finfo);
|
||||
}
|
||||
}
|
@@ -22,37 +22,54 @@ class Factory
|
||||
public const map = [
|
||||
'createtimestamp' => Internal\Timestamp::class,
|
||||
'creatorsname' => Internal\DN::class,
|
||||
'configcontext' => Schema\Generic::class,
|
||||
'contextcsn' => Internal\CSN::class,
|
||||
'entrycsn' => Internal\CSN::class,
|
||||
'entrydn' => Internal\DN::class,
|
||||
'entryuuid' => Internal\UUID::class,
|
||||
'etag' => Internal\Etag::class,
|
||||
'krblastfailedauth' => Attribute\NoAttrTags\Generic::class,
|
||||
'krblastpwdchange' => Attribute\NoAttrTags\Generic::class,
|
||||
'krblastsuccessfulauth' => Attribute\NoAttrTags\Generic::class,
|
||||
'krbpasswordexpiration' => Attribute\NoAttrTags\Generic::class,
|
||||
'krbloginfailedcount' => Attribute\NoAttrTags\Generic::class,
|
||||
'krbprincipalkey' => KrbPrincipalKey::class,
|
||||
'krbticketflags' => KrbTicketFlags::class,
|
||||
'gidnumber' => GidNumber::class,
|
||||
'hassubordinates' => Internal\HasSubordinates::class,
|
||||
'jpegphoto' => Binary\JpegPhoto::class,
|
||||
'modifytimestamp' => Internal\Timestamp::class,
|
||||
'modifiersname' => Internal\DN::class,
|
||||
'monitorcontext' => Schema\Generic::class,
|
||||
'namingcontexts' => Schema\Generic::class,
|
||||
'numsubordinates' => Internal\NumSubordinates::class,
|
||||
'objectclass' => ObjectClass::class,
|
||||
'pwdpolicysubentry' => Internal\PwdPolicySubentry::class,
|
||||
'structuralobjectclass' => Internal\StructuralObjectClass::class,
|
||||
'subschemasubentry' => Internal\SubschemaSubentry::class,
|
||||
'supportedcontrol' => Schema\OID::class,
|
||||
'supportedextension' => Schema\OID::class,
|
||||
'supportedfeatures' => Schema\OID::class,
|
||||
'supportedldapversion' => Schema\Generic::class,
|
||||
'supportedsaslmechanisms' => Schema\Mechanisms::class,
|
||||
'usercertificate' => UserCertificate::class,
|
||||
'userpassword' => Password::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Create the new Object for an attribute
|
||||
*
|
||||
* @param string $dn
|
||||
* @param string $attribute
|
||||
* @param array $values
|
||||
* @param array $oc
|
||||
* @return Attribute
|
||||
*/
|
||||
public static function create(string $attribute,array $values): Attribute
|
||||
public static function create(string $dn,string $attribute,array $values,array $oc=[]): Attribute
|
||||
{
|
||||
$class = Arr::get(self::map,strtolower($attribute),Attribute::class);
|
||||
Log::debug(sprintf('%s:Creating LDAP Attribute [%s] as [%s]',static::LOGKEY,$attribute,$class));
|
||||
|
||||
return new $class($attribute,$values);
|
||||
return new $class($dn,$attribute,$values,$oc);
|
||||
}
|
||||
}
|
@@ -9,4 +9,5 @@ use App\Classes\LDAP\Attribute;
|
||||
*/
|
||||
final class GidNumber extends Attribute
|
||||
{
|
||||
protected(set) bool $no_attr_tags = FALSE;
|
||||
}
|
@@ -11,7 +11,8 @@ use App\Classes\LDAP\Attribute;
|
||||
*/
|
||||
abstract class Internal extends Attribute
|
||||
{
|
||||
protected bool $is_internal = TRUE;
|
||||
protected(set) bool $is_internal = TRUE;
|
||||
protected(set) bool $no_attr_tags = TRUE;
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||
{
|
||||
|
12
app/Classes/LDAP/Attribute/Internal/Etag.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/Etag.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
/**
|
||||
* Represents an Etag Attribute
|
||||
*/
|
||||
final class Etag extends Internal
|
||||
{
|
||||
}
|
12
app/Classes/LDAP/Attribute/Internal/NumSubordinates.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/NumSubordinates.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
/**
|
||||
* Represents an NumSubordinates Attribute
|
||||
*/
|
||||
final class NumSubordinates extends Internal
|
||||
{
|
||||
}
|
12
app/Classes/LDAP/Attribute/Internal/PwdPolicySubentry.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/PwdPolicySubentry.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
|
||||
/**
|
||||
* Represents an PwdPolicySubentry Attribute
|
||||
*/
|
||||
final class PwdPolicySubentry extends Internal
|
||||
{
|
||||
}
|
42
app/Classes/LDAP/Attribute/KrbPrincipalKey.php
Normal file
42
app/Classes/LDAP/Attribute/KrbPrincipalKey.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
/**
|
||||
* Represents an attribute whose values are passwords
|
||||
*/
|
||||
final class KrbPrincipalKey extends Attribute
|
||||
{
|
||||
use MD5Updates;
|
||||
|
||||
protected(set) bool $no_attr_tags = TRUE;
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||
{
|
||||
return view('components.attribute.krbprincipalkey')
|
||||
->with('o',$this)
|
||||
->with('edit',$edit)
|
||||
->with('old',$old)
|
||||
->with('new',$new);
|
||||
}
|
||||
|
||||
public function render_item_old(string $dotkey): ?string
|
||||
{
|
||||
return parent::render_item_old($dotkey)
|
||||
? str_repeat('*',16)
|
||||
: NULL;
|
||||
}
|
||||
|
||||
public function render_item_new(string $dotkey): ?string
|
||||
{
|
||||
return parent::render_item_new($dotkey)
|
||||
? str_repeat('*',16)
|
||||
: NULL;
|
||||
}
|
||||
}
|
61
app/Classes/LDAP/Attribute/KrbTicketFlags.php
Normal file
61
app/Classes/LDAP/Attribute/KrbTicketFlags.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
|
||||
/**
|
||||
* Represents an attribute whose value is a Kerberos Ticket Flag
|
||||
* See RFC4120
|
||||
*/
|
||||
final class KrbTicketFlags extends Attribute
|
||||
{
|
||||
protected(set) bool $no_attr_tags = TRUE;
|
||||
|
||||
private const DISALLOW_POSTDATED = 0x00000001;
|
||||
private const DISALLOW_FORWARDABLE = 0x00000002;
|
||||
private const DISALLOW_TGT_BASED = 0x00000004;
|
||||
private const DISALLOW_RENEWABLE = 0x00000008;
|
||||
private const DISALLOW_PROXIABLE = 0x00000010;
|
||||
private const DISALLOW_DUP_SKEY = 0x00000020;
|
||||
private const DISALLOW_ALL_TIX = 0x00000040;
|
||||
private const REQUIRES_PRE_AUTH = 0x00000080;
|
||||
private const REQUIRES_HW_AUTH = 0x00000100;
|
||||
private const REQUIRES_PWCHANGE = 0x00000200;
|
||||
private const DISALLOW_SVR = 0x00001000;
|
||||
private const PWCHANGE_SERVICE = 0x00002000;
|
||||
|
||||
private static function helpers(): Collection
|
||||
{
|
||||
$helpers = collect([
|
||||
log(self::DISALLOW_POSTDATED,2) => __('KRB_DISALLOW_POSTDATED'),
|
||||
log(self::DISALLOW_FORWARDABLE,2) => __('KRB_DISALLOW_FORWARDABLE'),
|
||||
log(self::DISALLOW_TGT_BASED,2) => __('KRB_DISALLOW_TGT_BASED'),
|
||||
log(self::DISALLOW_RENEWABLE,2) => __('KRB_DISALLOW_RENEWABLE'),
|
||||
log(self::DISALLOW_PROXIABLE,2) => __('KRB_DISALLOW_PROXIABLE'),
|
||||
log(self::DISALLOW_DUP_SKEY,2) => __('KRB_DISALLOW_DUP_SKEY'),
|
||||
log(self::DISALLOW_ALL_TIX,2) => __('KRB_DISALLOW_ALL_TIX'),
|
||||
log(self::REQUIRES_PRE_AUTH,2) => __('KRB_REQUIRES_PRE_AUTH'),
|
||||
log(self::REQUIRES_HW_AUTH,2) => __('KRB_REQUIRES_HW_AUTH'),
|
||||
log(self::REQUIRES_PWCHANGE,2) => __('KRB_REQUIRES_PWCHANGE'),
|
||||
log(self::DISALLOW_SVR,2) => __('KRB_DISALLOW_SVR'),
|
||||
log(self::PWCHANGE_SERVICE,2) => __('KRB_PWCHANGE_SERVICE'),
|
||||
])
|
||||
->replace(config('pla.krb.bits',[]));
|
||||
|
||||
return $helpers;
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||
{
|
||||
return view('components.attribute.krbticketflags')
|
||||
->with('o',$this)
|
||||
->with('edit',$edit)
|
||||
->with('old',$old)
|
||||
->with('new',$new)
|
||||
->with('helper',static::helpers());
|
||||
}
|
||||
}
|
13
app/Classes/LDAP/Attribute/NoAttrTags/Generic.php
Normal file
13
app/Classes/LDAP/Attribute/NoAttrTags/Generic.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\NoAttrTags;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
|
||||
/**
|
||||
* Represents an Attribute that doesnt have Lang Tags
|
||||
*/
|
||||
class Generic extends Attribute
|
||||
{
|
||||
protected(set) bool $no_attr_tags = TRUE;
|
||||
}
|
@@ -6,32 +6,57 @@ use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
/**
|
||||
* Represents an ObjectClass Attribute
|
||||
*/
|
||||
final class ObjectClass extends Attribute
|
||||
{
|
||||
protected(set) bool $no_attr_tags = TRUE;
|
||||
|
||||
// The schema ObjectClasses for this objectclass of a DN
|
||||
protected Collection $oc_schema;
|
||||
|
||||
public function __construct(string $name,array $values)
|
||||
/**
|
||||
* Create an ObjectClass Attribute
|
||||
*
|
||||
* @param string $dn DN this attribute is used in
|
||||
* @param string $name Name of the attribute
|
||||
* @param array $values Current Values
|
||||
* @param array $oc The objectclasses that the DN of this attribute has (ignored for objectclasses)
|
||||
*/
|
||||
public function __construct(string $dn,string $name,array $values,array $oc=[])
|
||||
{
|
||||
parent::__construct($name,$values);
|
||||
parent::__construct($dn,$name,$values,['top']);
|
||||
|
||||
$this->oc_schema = config('server')
|
||||
->schema('objectclasses')
|
||||
->filter(fn($item)=>$this->values->contains($item->name));
|
||||
$this->set_oc_schema($this->tagValuesOld());
|
||||
}
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
return match ($key) {
|
||||
'structural' => $this->oc_schema->filter(fn($item) => $item->isStructural()),
|
||||
'structural' => $this->oc_schema->filter(fn($item)=>$item->isStructural()),
|
||||
default => parent::__get($key),
|
||||
};
|
||||
}
|
||||
|
||||
public function __set(string $key,mixed $values): void
|
||||
{
|
||||
switch ($key) {
|
||||
case 'values':
|
||||
parent::__set($key,$values);
|
||||
|
||||
// We need to populate oc_schema, if we are a new OC and thus dont have any old values
|
||||
if (! $this->values_old->count() && $this->values->count())
|
||||
$this->set_oc_schema($this->tagValues());
|
||||
|
||||
break;
|
||||
|
||||
default: parent::__set($key,$values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a specific value the structural objectclass
|
||||
*
|
||||
@@ -50,7 +75,15 @@ final class ObjectClass extends Attribute
|
||||
return view('components.attribute.objectclass')
|
||||
->with('o',$this)
|
||||
->with('edit',$edit)
|
||||
->with('langtag',Entry::TAG_NOTAG)
|
||||
->with('old',$old)
|
||||
->with('new',$new);
|
||||
}
|
||||
|
||||
private function set_oc_schema(Collection $tv): void
|
||||
{
|
||||
$this->oc_schema = config('server')
|
||||
->schema('objectclasses')
|
||||
->filter(fn($item)=>$tv->contains($item->name));
|
||||
}
|
||||
}
|
@@ -15,6 +15,9 @@ use App\Traits\MD5Updates;
|
||||
final class Password extends Attribute
|
||||
{
|
||||
use MD5Updates;
|
||||
|
||||
protected(set) bool $no_attr_tags = TRUE;
|
||||
|
||||
private const password_helpers = 'Classes/LDAP/Attribute/Password';
|
||||
public const commands = 'App\\Classes\\LDAP\\Attribute\\Password\\';
|
||||
|
||||
@@ -85,19 +88,23 @@ final class Password extends Attribute
|
||||
->with('helpers',static::helpers()->map(fn($item,$key)=>['id'=>$key,'value'=>$key])->sort());
|
||||
}
|
||||
|
||||
public function render_item_old(int $key): ?string
|
||||
public function render_item_old(string $dotkey): ?string
|
||||
{
|
||||
$pw = Arr::get($this->oldValues,$key);
|
||||
$pw = parent::render_item_old($dotkey);
|
||||
|
||||
return $pw
|
||||
? (((($x=$this->hash($pw)) && ($x::id() !== '*clear*')) ? sprintf('{%s}',$x::shortid()) : '').str_repeat('*',16))
|
||||
? (((($x=$this->hash($pw)) && ($x::id() === '*clear*')) ? sprintf('{%s}',$x::shortid()) : '')
|
||||
.str_repeat('*',16))
|
||||
: NULL;
|
||||
}
|
||||
|
||||
public function render_item_new(int $key): ?string
|
||||
public function render_item_new(string $dotkey): ?string
|
||||
{
|
||||
$pw = Arr::get($this->values,$key);
|
||||
$pw = parent::render_item_new($dotkey);
|
||||
|
||||
return $pw
|
||||
? (((($x=$this->hash($pw)) && ($x::id() !== '*clear*')) ? sprintf('{%s}',$x::shortid()) : '').str_repeat('*',16))
|
||||
? (((($x=$this->hash($pw)) && ($x::id() !== '*clear*')) ? sprintf('{%s}',$x::shortid()) : '')
|
||||
.str_repeat('*',16))
|
||||
: NULL;
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ final class SMD5 extends Base
|
||||
return $source === $this->encode($compare,$this->salted_salt($source));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
public function encode(string $password,?string $salt=NULL): string
|
||||
{
|
||||
if (is_null($salt))
|
||||
$salt = hex2bin(random_salt(self::salt));
|
||||
|
@@ -12,7 +12,7 @@ final class SSHA extends Base
|
||||
return $source === $this->encode($compare,$this->salted_salt($source));
|
||||
}
|
||||
|
||||
public function encode(string $password,string $salt=NULL): string
|
||||
public function encode(string $password,?string $salt=NULL): string
|
||||
{
|
||||
return sprintf('{%s}%s',self::key,$this->salted_hash($password,'sha1',self::salt,$salt));
|
||||
}
|
||||
|
@@ -24,11 +24,11 @@ final class RDN extends Attribute
|
||||
};
|
||||
}
|
||||
|
||||
public function hints(): array
|
||||
public function hints(): Collection
|
||||
{
|
||||
return [
|
||||
return collect([
|
||||
'required' => __('RDN is required')
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||
|
@@ -14,6 +14,7 @@ use App\Classes\LDAP\Attribute;
|
||||
abstract class Schema extends Attribute
|
||||
{
|
||||
protected bool $internal = TRUE;
|
||||
protected(set) bool $no_attr_tags = TRUE;
|
||||
|
||||
protected static function _get(string $filename,string $string,string $key): ?string
|
||||
{
|
||||
@@ -30,7 +31,7 @@ abstract class Schema extends Attribute
|
||||
while (! feof($f)) {
|
||||
$line = trim(fgets($f));
|
||||
|
||||
if (! $line OR preg_match('/^#/',$line))
|
||||
if ((! $line) || preg_match('/^#/',$line))
|
||||
continue;
|
||||
|
||||
$fields = explode(':',$line);
|
||||
@@ -41,12 +42,15 @@ abstract class Schema extends Attribute
|
||||
'desc'=>Arr::get($fields,3,__('No description available, can you help with one?')),
|
||||
]);
|
||||
}
|
||||
|
||||
fclose($f);
|
||||
|
||||
return $result;
|
||||
});
|
||||
|
||||
return Arr::get(($array ? $array->get($string) : []),$key);
|
||||
return Arr::get(($array ? $array->get($string) : []),
|
||||
$key,
|
||||
__('No description available, can you help with one?'));
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||
|
20
app/Classes/LDAP/Attribute/Schema/Generic.php
Normal file
20
app/Classes/LDAP/Attribute/Schema/Generic.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute\Schema;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Schema;
|
||||
|
||||
/**
|
||||
* Represents a Generic Schema Attribute
|
||||
*/
|
||||
class Generic extends Schema
|
||||
{
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||
{
|
||||
// @note Schema attributes cannot be edited
|
||||
return view('components.attribute.schema.generic')
|
||||
->with('o',$this);
|
||||
}
|
||||
}
|
52
app/Classes/LDAP/Attribute/UserCertificate.php
Normal file
52
app/Classes/LDAP/Attribute/UserCertificate.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
/**
|
||||
* Represents an attribute whose values is a binary user certificate
|
||||
*/
|
||||
final class UserCertificate extends Attribute
|
||||
{
|
||||
use MD5Updates;
|
||||
|
||||
private array $_object = [];
|
||||
|
||||
public function certificate(int $key=0): string
|
||||
{
|
||||
return sprintf("-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----",
|
||||
join("\n",str_split(base64_encode(Arr::get($this->values_old,'binary.'.$key)),80))
|
||||
);
|
||||
}
|
||||
|
||||
public function cert_info(string $index,int $key=0): mixed
|
||||
{
|
||||
if (! array_key_exists($key,$this->_object))
|
||||
$this->_object[$key] = openssl_x509_parse(openssl_x509_read($this->certificate($key)));
|
||||
|
||||
|
||||
return Arr::get($this->_object[$key],$index);
|
||||
}
|
||||
|
||||
public function expires($key=0): Carbon
|
||||
{
|
||||
return Carbon::createFromTimestampUTC($this->cert_info('validTo_time_t',$key));
|
||||
}
|
||||
|
||||
public function render_item_old(string $dotkey): ?string
|
||||
{
|
||||
return join("\n",str_split(base64_encode(parent::render_item_old($dotkey)),80));
|
||||
}
|
||||
|
||||
public function subject($key=0): string
|
||||
{
|
||||
$subject = collect($this->cert_info('subject',$key))->reverse();
|
||||
|
||||
return $subject->map(fn($item,$key)=>sprintf("%s=%s",$key,$item))->join(',');
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ namespace App\Classes\LDAP\Export;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Classes\LDAP\Export;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
/**
|
||||
* Export from LDAP using an LDIF format
|
||||
@@ -41,13 +42,25 @@ class LDIF extends Export
|
||||
|
||||
// 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);
|
||||
}
|
||||
if ($ao->no_attr_tags)
|
||||
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);
|
||||
}
|
||||
|
||||
else
|
||||
foreach ($ao->values as $tag => $tagvalues) {
|
||||
foreach ($tagvalues as $value) {
|
||||
$result .= $this->multiLineDisplay(
|
||||
Str::isAscii($value)
|
||||
? sprintf('%s: %s',$ao->name.(($tag !== Entry::TAG_NOTAG) ? ';'.$tag : ''),$value)
|
||||
: sprintf('%s:: %s',$ao->name.(($tag !== Entry::TAG_NOTAG) ? ';'.$tag : ''),base64_encode($value))
|
||||
,$this->br);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,9 +3,9 @@
|
||||
namespace App\Classes\LDAP;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use LdapRecord\LdapRecordException;
|
||||
|
||||
use App\Exceptions\Import\GeneralException;
|
||||
use App\Exceptions\Import\ObjectExistsException;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
/**
|
||||
@@ -48,7 +48,6 @@ abstract class Import
|
||||
* @param int $action
|
||||
* @return Collection
|
||||
* @throws GeneralException
|
||||
* @throws ObjectExistsException
|
||||
*/
|
||||
final protected function commit(Entry $o,int $action): Collection
|
||||
{
|
||||
@@ -57,15 +56,24 @@ abstract class Import
|
||||
try {
|
||||
$o->save();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return collect([
|
||||
'dn'=>$o->getDN(),
|
||||
'result'=>sprintf('%d: %s (%s)',
|
||||
($x=$e->getDetailedError())->getErrorCode(),
|
||||
$x->getErrorMessage(),
|
||||
$x->getDiagnosticMessage(),
|
||||
)
|
||||
]);
|
||||
} catch (LdapRecordException $e) {
|
||||
if ($e->getDetailedError())
|
||||
return collect([
|
||||
'dn'=>$o->getDN(),
|
||||
'result'=>sprintf('%d: %s (%s)',
|
||||
($x=$e->getDetailedError())->getErrorCode(),
|
||||
$x->getErrorMessage(),
|
||||
$x->getDiagnosticMessage(),
|
||||
)
|
||||
]);
|
||||
else
|
||||
return collect([
|
||||
'dn'=>$o->getDN(),
|
||||
'result'=>sprintf('%d: %s',
|
||||
$e->getCode(),
|
||||
$e->getMessage(),
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
return collect(['dn'=>$o->getDN(),'result'=>__('Created')]);
|
||||
|
@@ -46,7 +46,7 @@ class LDIF extends Import
|
||||
if (! $line) {
|
||||
if (! is_null($o)) {
|
||||
// Add the last attribute;
|
||||
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||
$o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||
|
||||
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||
|
||||
@@ -59,8 +59,6 @@ class LDIF extends Import
|
||||
$base64encoded = FALSE;
|
||||
$attribute = NULL;
|
||||
$value = '';
|
||||
|
||||
// Else its a blank line
|
||||
}
|
||||
|
||||
continue;
|
||||
@@ -68,8 +66,9 @@ class LDIF extends Import
|
||||
|
||||
$m = [];
|
||||
preg_match('/^([a-zA-Z0-9;-]+)(:+)\s+(.*)$/',$line,$m);
|
||||
dump(['m'=>$m,'line'=>$line]);
|
||||
|
||||
switch ($x=Arr::get($m,1)) {
|
||||
switch (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));
|
||||
@@ -125,7 +124,7 @@ class LDIF extends Import
|
||||
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);
|
||||
$o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||
else
|
||||
throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c));
|
||||
}
|
||||
@@ -133,7 +132,6 @@ class LDIF extends Import
|
||||
|
||||
// Start of a new attribute
|
||||
$base64encoded = ($m[2] === '::');
|
||||
// @todo Need to parse attributes with ';' options
|
||||
$attribute = $m[1];
|
||||
$value = $m[3];
|
||||
|
||||
@@ -147,7 +145,7 @@ class LDIF extends Import
|
||||
// We may still have a pending action
|
||||
if ($action) {
|
||||
// Add the last attribute;
|
||||
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||
$o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||
|
||||
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||
|
||||
@@ -159,8 +157,8 @@ class LDIF extends Import
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function readEntry() {
|
||||
static $haveVersion = false;
|
||||
public function xreadEntry() {
|
||||
static $haveVersion = FALSE;
|
||||
|
||||
if ($lines = $this->nextLines()) {
|
||||
|
||||
@@ -179,7 +177,7 @@ class LDIF extends Import
|
||||
} else
|
||||
$changetype = 'add';
|
||||
|
||||
$this->template = new Template($this->server_id,null,null,$changetype);
|
||||
$this->template = new Template($this->server_id,NULL,NULL,$changetype);
|
||||
|
||||
switch ($changetype) {
|
||||
case 'add':
|
||||
@@ -201,7 +199,7 @@ class LDIF extends Import
|
||||
return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines);
|
||||
|
||||
$this->template->setDN($dn);
|
||||
$this->template->accept(false,true);
|
||||
$this->template->accept(FALSE,TRUE);
|
||||
|
||||
return $this->getModifyDetails($lines);
|
||||
|
||||
@@ -221,13 +219,13 @@ class LDIF extends Import
|
||||
|
||||
default:
|
||||
if (! $server->dnExists($dn))
|
||||
return $this->error(_('Unkown change type'),$lines);
|
||||
return $this->error(_('Unknown change type'),$lines);
|
||||
}
|
||||
|
||||
} else
|
||||
return $this->error(_('A valid dn line is required'),$lines);
|
||||
|
||||
} else
|
||||
return false;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
@@ -6,6 +6,9 @@ use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
/**
|
||||
* Represents an LDAP AttributeType
|
||||
*
|
||||
@@ -320,11 +323,12 @@ final class AttributeType extends Base {
|
||||
* that is the list of objectClasses which must have this attribute.
|
||||
*
|
||||
* @param string $name The name of the objectClass to add.
|
||||
* @param bool $structural
|
||||
*/
|
||||
public function addRequiredByObjectClass(string $name): void
|
||||
public function addRequiredByObjectClass(string $name,bool $structural): void
|
||||
{
|
||||
if (! $this->required_by_object_classes->contains($name))
|
||||
$this->required_by_object_classes->push($name);
|
||||
if (! $this->required_by_object_classes->has($name))
|
||||
$this->required_by_object_classes->put($name,$structural);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,6 +336,7 @@ final class AttributeType extends Base {
|
||||
* that is the list of objectClasses which provide this attribute.
|
||||
*
|
||||
* @param string $name The name of the objectClass to add.
|
||||
* @param bool $structural
|
||||
*/
|
||||
public function addUsedInObjectClass(string $name,bool $structural): void
|
||||
{
|
||||
@@ -339,6 +344,11 @@ final class AttributeType extends Base {
|
||||
$this->used_in_object_classes->put($name,$structural);
|
||||
}
|
||||
|
||||
private function factory(): Attribute
|
||||
{
|
||||
return Attribute\Factory::create(dn:'',attribute:$this->name,values:[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of attributes that are an alias for this attribute (if any).
|
||||
*
|
||||
@@ -476,6 +486,28 @@ final class AttributeType extends Base {
|
||||
return $this->used_in_object_classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a list of objectclasses return all parent objectclasses as well
|
||||
*
|
||||
* @param Collection $ocs
|
||||
* @return Collection
|
||||
*/
|
||||
public function heirachy(Collection $ocs): Collection
|
||||
{
|
||||
$result = collect();
|
||||
|
||||
foreach ($ocs as $oc) {
|
||||
$schema = config('server')
|
||||
->schema('objectclasses',$oc)
|
||||
->getParents(TRUE)
|
||||
->pluck('name');
|
||||
|
||||
$result = $result->merge($schema)->push($oc);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @deprecated use $this->forced_as_may
|
||||
@@ -544,21 +576,22 @@ final class AttributeType extends Base {
|
||||
*/
|
||||
public function validation(array $array): ?array
|
||||
{
|
||||
// For each item in array, we need to get the OC heirachy
|
||||
$heirachy = collect($array)
|
||||
->filter()
|
||||
->map(fn($item)=>config('server')
|
||||
->schema('objectclasses',$item)
|
||||
->getSupClasses()
|
||||
->push($item))
|
||||
// For each item in array, we need to get the OC hierarchy
|
||||
$heirachy = $this->heirachy(collect($array)
|
||||
->flatten()
|
||||
->unique();
|
||||
->filter());
|
||||
|
||||
// Get any config validation
|
||||
$validation = collect(Arr::get(config('ldap.validation'),$this->name_lc,[]));
|
||||
if (($heirachy->intersect($this->required_by_object_classes)->count() > 0)
|
||||
|
||||
$nolangtag = sprintf('%s.%s.0',$this->name_lc,Entry::TAG_NOTAG);
|
||||
|
||||
// Add in schema required by conditions
|
||||
if (($heirachy->intersect($this->required_by_object_classes->keys())->count() > 0)
|
||||
&& (! collect($validation->get($this->name_lc))->contains('required'))) {
|
||||
$validation->put($this->name_lc,array_merge(['required','min:1'],$validation->get($this->name_lc,[])))
|
||||
->put($this->name_lc.'.*',array_merge(['required','min:1'],$validation->get($this->name_lc.'.*',[])));
|
||||
$validation
|
||||
->prepend(array_merge(['required','min:1'],$validation->get($nolangtag,[])),$nolangtag)
|
||||
->prepend(array_merge(['required','array','min:1',($this->factory()->no_attr_tags ? 'max:1' : NULL)],$validation->get($this->name_lc,[])),$this->name_lc);
|
||||
}
|
||||
|
||||
return $validation->toArray();
|
||||
|
@@ -209,7 +209,8 @@ final class Server
|
||||
/**
|
||||
* Obtain the rootDSE for the server, that gives us server information
|
||||
*
|
||||
* @param null $connection
|
||||
* @param string|null $connection
|
||||
* @param Carbon|null $cachetime
|
||||
* @return Entry|null
|
||||
* @throws ObjectNotFoundException
|
||||
* @testedin TranslateOidTest::testRootDSE();
|
||||
@@ -230,7 +231,7 @@ final class Server
|
||||
/**
|
||||
* Get the Schema DN
|
||||
*
|
||||
* @param $connection
|
||||
* @param string|null $connection
|
||||
* @return string
|
||||
* @throws ObjectNotFoundException
|
||||
*/
|
||||
@@ -245,16 +246,21 @@ final class Server
|
||||
* Query the server for a DN and return its children and if those children have children.
|
||||
*
|
||||
* @param string $dn
|
||||
* @param array $attrs
|
||||
* @return LDAPCollection|NULL
|
||||
*/
|
||||
public function children(string $dn): ?LDAPCollection
|
||||
public function children(string $dn,array $attrs=['dn']): ?LDAPCollection
|
||||
{
|
||||
return ($x=(new Entry)
|
||||
->on($this->connection)
|
||||
->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
||||
->select(['*','hassubordinates'])
|
||||
->select(array_merge($attrs,[
|
||||
'hassubordinates', // Needed for the tree to know if an entry has children
|
||||
'c' // Needed for the tree to show icons for countries
|
||||
]))
|
||||
->setDn($dn)
|
||||
->list()
|
||||
->orderBy('dn')
|
||||
->get()) ? $x : NULL;
|
||||
}
|
||||
|
||||
@@ -428,7 +434,7 @@ final class Server
|
||||
// Add Required By.
|
||||
foreach ($must_attrs as $attr_name)
|
||||
if ($this->attributetypes->has(strtolower($attr_name)))
|
||||
$this->attributetypes[strtolower($attr_name)]->addRequiredByObjectClass($object_class->name);
|
||||
$this->attributetypes[strtolower($attr_name)]->addRequiredByObjectClass($object_class->name,$object_class->isStructural());
|
||||
|
||||
// Force May
|
||||
foreach ($object_class->getForceMayAttrs() as $attr_name)
|
||||
@@ -528,7 +534,6 @@ final class Server
|
||||
*
|
||||
* @param string $oid
|
||||
* @return LDAPSyntax|null
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function schemaSyntaxName(string $oid): ?LDAPSyntax
|
||||
{
|
||||
|
@@ -39,14 +39,13 @@ class APIController extends Controller
|
||||
*/
|
||||
public function children(Request $request): Collection
|
||||
{
|
||||
$levels = $request->query('depth',1);
|
||||
$dn = Crypt::decryptString($request->query('key'));
|
||||
|
||||
// Sometimes our key has a command, so we'll ignore it
|
||||
if (str_starts_with($dn,'*') && ($x=strpos($dn,'|')))
|
||||
$dn = substr($dn,$x+1);
|
||||
|
||||
Log::debug(sprintf('%s: Query [%s] - Levels [%d]',__METHOD__,$dn,$levels));
|
||||
Log::debug(sprintf('%s: Query [%s]',__METHOD__,$dn));
|
||||
|
||||
return (config('server'))
|
||||
->children($dn)
|
||||
@@ -98,11 +97,10 @@ class APIController extends Controller
|
||||
/**
|
||||
* Return the required and additional attributes for an object class
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $objectclass
|
||||
* @return array
|
||||
*/
|
||||
public function schema_objectclass_attrs(Request $request,string $objectclass): array
|
||||
public function schema_objectclass_attrs(string $objectclass): array
|
||||
{
|
||||
$oc = config('server')->schema('objectclasses',$objectclass);
|
||||
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -42,24 +41,14 @@ class HomeController extends Controller
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug Page
|
||||
*
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function debug()
|
||||
{
|
||||
return view('debug');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new object in the LDAP server
|
||||
*
|
||||
* @param EntryAddRequest $request
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||
* @return View
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function entry_add(EntryAddRequest $request)
|
||||
public function entry_add(EntryAddRequest $request): \Illuminate\View\View
|
||||
{
|
||||
if (! old('step',$request->validated('step')))
|
||||
abort(404);
|
||||
@@ -68,11 +57,12 @@ class HomeController extends Controller
|
||||
|
||||
$o = new Entry;
|
||||
|
||||
if (count(array_filter($x=old('objectclass',$request->objectclass)))) {
|
||||
if (count($x=array_filter(old('objectclass',$request->objectclass)))) {
|
||||
$o->objectclass = $x;
|
||||
|
||||
// Also add in our required attributes
|
||||
foreach($o->getAvailableAttributes()->filter(fn($item)=>$item->required) as $ao)
|
||||
$o->addAttribute($ao,'');
|
||||
$o->{$ao->name} = [Entry::TAG_NOTAG=>''];
|
||||
|
||||
$o->setRDNBase($key['dn']);
|
||||
}
|
||||
@@ -92,24 +82,26 @@ class HomeController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $id
|
||||
* @return \Closure|\Illuminate\Contracts\View\View|string
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function entry_attr_add(Request $request,string $id): string
|
||||
public function entry_attr_add(Request $request,string $id): \Illuminate\View\View
|
||||
{
|
||||
$xx = new \stdClass;
|
||||
$xx->index = 0;
|
||||
|
||||
$x = $request->noheader
|
||||
? (string)view(sprintf('components.attribute.widget.%s',$id))
|
||||
->with('o',Factory::create($id,[]))
|
||||
->with('value',$request->value)
|
||||
->with('loop',$xx)
|
||||
: (new AttributeType(Factory::create($id,[]),TRUE,collect($request->oc ?: [])))->render();
|
||||
$dn = $request->dn ? Crypt::decrypt($request->dn) : '';
|
||||
|
||||
return $x;
|
||||
return $request->noheader
|
||||
? view(sprintf('components.attribute.widget.%s',$id))
|
||||
->with('o',Factory::create(dn: $dn,attribute: $id,values: [],oc: $request->objectclasses))
|
||||
->with('value',$request->value)
|
||||
->with('langtag',Entry::TAG_NOTAG)
|
||||
->with('loop',$xx)
|
||||
: new AttributeType(Factory::create($dn,$id,[],$request->objectclasses),new: TRUE,edit: TRUE)
|
||||
->render();
|
||||
}
|
||||
|
||||
public function entry_create(EntryAddRequest $request)
|
||||
public function entry_create(EntryAddRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$key = $this->request_key($request,collect(old()));
|
||||
|
||||
@@ -139,24 +131,21 @@ class HomeController extends Controller
|
||||
|
||||
// @todo when we create an entry, and it already exists, enable a redirect to it
|
||||
} catch (LdapRecordException $e) {
|
||||
$request->flash();
|
||||
|
||||
switch ($x=$e->getDetailedError()->getErrorCode()) {
|
||||
case 8:
|
||||
return Redirect::to('/')
|
||||
->withInput()
|
||||
->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage())));
|
||||
|
||||
default:
|
||||
abort(599,$e->getDetailedError()->getErrorMessage());
|
||||
}
|
||||
return Redirect::back()
|
||||
->withInput()
|
||||
->withErrors(sprintf('%s: %s - %s: %s',
|
||||
__('LDAP Server Error Code'),
|
||||
$e->getDetailedError()->getErrorCode(),
|
||||
__($e->getDetailedError()->getErrorMessage()),
|
||||
$e->getDetailedError()->getDiagnosticMessage(),
|
||||
));
|
||||
}
|
||||
|
||||
return Redirect::to('/')
|
||||
->withFragment($o->getDNSecure());
|
||||
}
|
||||
|
||||
public function entry_delete(Request $request)
|
||||
public function entry_delete(Request $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$dn = Crypt::decryptString($request->dn);
|
||||
|
||||
@@ -196,14 +185,12 @@ class HomeController extends Controller
|
||||
->with('success',[sprintf('%s: %s',__('Deleted'),$dn)]);
|
||||
}
|
||||
|
||||
public function entry_export(Request $request,string $id)
|
||||
public function entry_export(Request $request,string $id): \Illuminate\View\View
|
||||
{
|
||||
$dn = Crypt::decryptString($id);
|
||||
|
||||
$result = (new Entry)
|
||||
->query()
|
||||
//->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
||||
//->select(['*'])
|
||||
->setDn($dn)
|
||||
->recursive()
|
||||
->get();
|
||||
@@ -215,12 +202,13 @@ class HomeController extends Controller
|
||||
/**
|
||||
* Render an available list of objectclasses for an Entry
|
||||
*
|
||||
* @param string $id
|
||||
* @return mixed
|
||||
* @param Request $request
|
||||
* @return Collection
|
||||
*/
|
||||
public function entry_objectclass_add(Request $request)
|
||||
public function entry_objectclass_add(Request $request): Collection
|
||||
{
|
||||
$oc = Factory::create('objectclass',$request->oc);
|
||||
$dn = $request->key ? Crypt::decryptString($request->dn) : '';
|
||||
$oc = Factory::create($dn,'objectclass',$request->oc);
|
||||
|
||||
$ocs = $oc
|
||||
->structural
|
||||
@@ -242,7 +230,7 @@ class HomeController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function entry_password_check(Request $request)
|
||||
public function entry_password_check(Request $request): Collection
|
||||
{
|
||||
$dn = Crypt::decryptString($request->dn);
|
||||
$o = config('server')->fetch($dn);
|
||||
@@ -250,7 +238,7 @@ class HomeController extends Controller
|
||||
$password = $o->getObject('userpassword');
|
||||
|
||||
$result = collect();
|
||||
foreach ($password as $key => $value) {
|
||||
foreach ($password->values->dot() as $key => $value) {
|
||||
$hash = $password->hash($value);
|
||||
$compare = Arr::get($request->password,$key);
|
||||
//Log::debug(sprintf('comparing [%s] with [%s] type [%s]',$value,$compare,$hash::id()),['object'=>$hash]);
|
||||
@@ -265,10 +253,10 @@ class HomeController extends Controller
|
||||
* Show a confirmation to update a DN
|
||||
*
|
||||
* @param EntryRequest $request
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
* @throws ObjectNotFoundException
|
||||
*/
|
||||
public function entry_pending_update(EntryRequest $request)
|
||||
public function entry_pending_update(EntryRequest $request): \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
{
|
||||
$dn = Crypt::decryptString($request->dn);
|
||||
|
||||
@@ -277,22 +265,27 @@ class HomeController extends Controller
|
||||
foreach ($request->except(['_token','dn','userpassword_hash','userpassword']) as $key => $value)
|
||||
$o->{$key} = array_filter($value,fn($item)=>! is_null($item));
|
||||
|
||||
// @todo Need to handle incoming attributes that were modified by MD5Updates Trait (eg: jpegphoto)
|
||||
|
||||
// We need to process and encrypt the password
|
||||
if ($request->userpassword) {
|
||||
$passwords = [];
|
||||
foreach ($request->userpassword as $key => $value) {
|
||||
$po = $o->getObject('userpassword');
|
||||
foreach (Arr::dot($request->userpassword) as $dotkey => $value) {
|
||||
// If the password is still the MD5 of the old password, then it hasnt changed
|
||||
if (($old=Arr::get($o->userpassword,$key)) && ($value === md5($old))) {
|
||||
array_push($passwords,$old);
|
||||
if (($old=Arr::get($po,$dotkey)) && ($value === md5($old))) {
|
||||
$passwords[$dotkey] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value) {
|
||||
$type = Arr::get($request->userpassword_hash,$key);
|
||||
array_push($passwords,Password::hash_id($type)->encode($value));
|
||||
$type = Arr::get($request->userpassword_hash,$dotkey);
|
||||
$passwords[$dotkey] = Password::hash_id($type)
|
||||
->encode($value);
|
||||
}
|
||||
}
|
||||
$o->userpassword = $passwords;
|
||||
|
||||
$o->userpassword = Arr::undot($passwords);
|
||||
}
|
||||
|
||||
if (! $o->getDirty())
|
||||
@@ -312,8 +305,10 @@ class HomeController extends Controller
|
||||
* @param EntryRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws ObjectNotFoundException
|
||||
* @todo When removing an attribute value, from a multi-value attribute, we have a ghost record showing after the update
|
||||
* @todo Need to check when removing a single attribute value, do we have a ghost as well? Might be because we are redirecting with input?
|
||||
*/
|
||||
public function entry_update(EntryRequest $request)
|
||||
public function entry_update(EntryRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$dn = Crypt::decryptString($request->dn);
|
||||
|
||||
@@ -344,22 +339,19 @@ class HomeController extends Controller
|
||||
}
|
||||
|
||||
} catch (LdapRecordException $e) {
|
||||
$request->flash();
|
||||
|
||||
switch ($x=$e->getDetailedError()->getErrorCode()) {
|
||||
case 8:
|
||||
return Redirect::to('/')
|
||||
->withInput()
|
||||
->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage())));
|
||||
|
||||
default:
|
||||
abort(599,$e->getDetailedError()->getErrorMessage());
|
||||
}
|
||||
return Redirect::to('/')
|
||||
->withInput()
|
||||
->withErrors(sprintf('%s: %s - %s: %s',
|
||||
__('LDAP Server Error Code'),
|
||||
$e->getDetailedError()->getErrorCode(),
|
||||
__($e->getDetailedError()->getErrorMessage()),
|
||||
$e->getDetailedError()->getDiagnosticMessage(),
|
||||
));
|
||||
}
|
||||
|
||||
return Redirect::to('/')
|
||||
->withInput()
|
||||
->with('updated',collect($dirty)->map(fn($key,$item)=>$o->getObject($item)));
|
||||
->with('updated',collect($dirty)->map(fn($item,$key)=>$o->getObject(collect(explode(';',$key))->first())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,9 +360,9 @@ class HomeController extends Controller
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Collection|null $old
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|View
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function frame(Request $request,?Collection $old=NULL): View
|
||||
public function frame(Request $request,?Collection $old=NULL): \Illuminate\View\View
|
||||
{
|
||||
// If our index was not render from a root url, then redirect to it
|
||||
if (($request->root().'/' !== url()->previous()) && $request->method() === 'POST')
|
||||
@@ -383,6 +375,12 @@ class HomeController extends Controller
|
||||
: view('frames.'.$key['cmd']))
|
||||
->with('bases',$this->bases());
|
||||
|
||||
// If we are rendering a DN, rebuild our object
|
||||
$o = config('server')->fetch($key['dn']);
|
||||
|
||||
foreach (collect(old())->except(['key','dn','step','_token','userpassword_hash']) as $attr => $value)
|
||||
$o->{$attr} = $value;
|
||||
|
||||
return match ($key['cmd']) {
|
||||
'create' => $view
|
||||
->with('container',old('container',$key['dn']))
|
||||
@@ -390,7 +388,14 @@ class HomeController extends Controller
|
||||
|
||||
'dn' => $view
|
||||
->with('dn',$key['dn'])
|
||||
->with('page_actions',collect(['edit'=>TRUE,'copy'=>TRUE])),
|
||||
->with('o',$o)
|
||||
->with('page_actions',collect([
|
||||
'copy'=>FALSE,
|
||||
'create'=>FALSE,
|
||||
'delete'=>TRUE,
|
||||
'edit'=>TRUE,
|
||||
'export'=>TRUE,
|
||||
])),
|
||||
|
||||
'import' => $view,
|
||||
|
||||
@@ -401,7 +406,7 @@ class HomeController extends Controller
|
||||
/**
|
||||
* This is the main page render function
|
||||
*/
|
||||
public function home(Request $request)
|
||||
public function home(Request $request): \Illuminate\View\View
|
||||
{
|
||||
// Did we come here as a result of a redirect
|
||||
return count(old())
|
||||
@@ -415,11 +420,11 @@ class HomeController extends Controller
|
||||
*
|
||||
* @param ImportRequest $request
|
||||
* @param string $type
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application
|
||||
* @return \Illuminate\View\View
|
||||
* @throws GeneralException
|
||||
* @throws VersionException
|
||||
*/
|
||||
public function import(ImportRequest $request,string $type)
|
||||
public function import(ImportRequest $request,string $type): \Illuminate\View\View
|
||||
{
|
||||
switch ($type) {
|
||||
case 'ldif':
|
||||
@@ -447,22 +452,6 @@ class HomeController extends Controller
|
||||
->with('ldif',htmlspecialchars($x));
|
||||
}
|
||||
|
||||
public function import_frame()
|
||||
{
|
||||
return view('frames.import');
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP Server INFO
|
||||
*
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function info()
|
||||
{
|
||||
return view('frames.info')
|
||||
->with('s',config('server'));
|
||||
}
|
||||
|
||||
/**
|
||||
* For any incoming request, work out the command and DN involved
|
||||
*
|
||||
@@ -501,10 +490,10 @@ class HomeController extends Controller
|
||||
*
|
||||
* @note Our route will validate that types are valid.
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||
* @return \Illuminate\View\View
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function schema_frame(Request $request)
|
||||
public function schema_frame(Request $request): \Illuminate\View\View
|
||||
{
|
||||
// If an invalid key, we'll 404
|
||||
if ($request->type && $request->key && (! config('server')->schema($request->type)->has($request->key)))
|
||||
@@ -530,9 +519,9 @@ class HomeController extends Controller
|
||||
* Return the image for the logged in user or anonymous
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function user_image(Request $request)
|
||||
public function user_image(Request $request): \Illuminate\Http\Response
|
||||
{
|
||||
$image = NULL;
|
||||
$content = NULL;
|
||||
@@ -550,4 +539,4 @@ class HomeController extends Controller
|
||||
return response($image)
|
||||
->header('Content-Type',$content);
|
||||
}
|
||||
}
|
||||
}
|
@@ -25,6 +25,7 @@ class ApplicationSession
|
||||
{
|
||||
Config::set('server',new Server);
|
||||
|
||||
view()->share('server', Config::get('server'));
|
||||
view()->share('user', auth()->user() ?: new User);
|
||||
|
||||
return $next($request);
|
||||
|
@@ -34,10 +34,11 @@ class EntryAddRequest extends FormRequest
|
||||
if (request()->method() === 'GET')
|
||||
return [];
|
||||
|
||||
$r = request() ?: collect();
|
||||
return config('server')
|
||||
->schema('attributetypes')
|
||||
->intersectByKeys($this->request)
|
||||
->map(fn($item)=>$item->validation(request()->get('objectclass')))
|
||||
->intersectByKeys($r->all())
|
||||
->map(fn($item)=>$item->validation($r->get('objectclass',[])))
|
||||
->filter()
|
||||
->flatMap(fn($item)=>$item)
|
||||
->merge([
|
||||
@@ -60,6 +61,12 @@ class EntryAddRequest extends FormRequest
|
||||
'rdn_value' => 'required_if:step,2|string|min:1',
|
||||
'step' => 'int|min:1|max:2',
|
||||
'objectclass'=>[
|
||||
'required',
|
||||
'array',
|
||||
'min:1',
|
||||
'max:1',
|
||||
],
|
||||
'objectclass._null_'=>[
|
||||
'required',
|
||||
'array',
|
||||
'min:1',
|
||||
|
@@ -13,10 +13,12 @@ class EntryRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$r = request() ?: collect();
|
||||
|
||||
return config('server')
|
||||
->schema('attributetypes')
|
||||
->intersectByKeys($this->request)
|
||||
->map(fn($item)=>$item->validation(request()?->get('objectclass') ?: []))
|
||||
->intersectByKeys($r->all())
|
||||
->map(fn($item)=>$item->validation($r->get('objectclass',[])))
|
||||
->filter()
|
||||
->flatMap(fn($item)=>$item)
|
||||
->toArray();
|
||||
|
@@ -2,13 +2,14 @@
|
||||
|
||||
namespace App\Ldap;
|
||||
|
||||
use LdapRecord\Configuration\DomainConfiguration;
|
||||
use LdapRecord\Connection as ConnectionBase;
|
||||
use LdapRecord\LdapInterface;
|
||||
|
||||
class Connection extends ConnectionBase
|
||||
{
|
||||
|
||||
public function __construct($config = [], LdapInterface $ldap = null)
|
||||
public function __construct(DomainConfiguration|array $config=[],?LdapInterface $ldap=NULL)
|
||||
{
|
||||
parent::__construct($config,$ldap);
|
||||
|
||||
|
@@ -14,9 +14,20 @@ use App\Classes\LDAP\Export\LDIF;
|
||||
use App\Exceptions\Import\AttributeException;
|
||||
use App\Exceptions\InvalidUsage;
|
||||
|
||||
/**
|
||||
* An Entry in an LDAP server
|
||||
*
|
||||
* @notes https://ldap.com/ldap-dns-and-rdns
|
||||
*/
|
||||
class Entry extends Model
|
||||
{
|
||||
private const TAG_CHARS = 'a-zA-Z0-9-';
|
||||
private const TAG_CHARS_LANG = 'lang-['.self::TAG_CHARS.']';
|
||||
public const TAG_NOTAG = '_null_';
|
||||
|
||||
// Our Attribute objects
|
||||
private Collection $objects;
|
||||
/* @deprecated */
|
||||
private bool $noObjectAttributes = FALSE;
|
||||
// For new entries, this is the container that this entry will be stored in
|
||||
private string $rdnbase;
|
||||
@@ -43,13 +54,18 @@ class Entry extends Model
|
||||
/**
|
||||
* This function overrides getAttributes to use our collection of Attribute objects instead of the models attributes.
|
||||
*
|
||||
* This returns an array that should be consistent with $this->attributes
|
||||
*
|
||||
* @return array
|
||||
* @note $this->attributes may not be updated with changes
|
||||
*/
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return $this->objects
|
||||
->map(fn($item)=>$item->values)
|
||||
->flatMap(fn($item)=>
|
||||
($item->no_attr_tags)
|
||||
? [strtolower($item->name)=>$item->values]
|
||||
: $item->values
|
||||
->flatMap(fn($v,$k)=>[strtolower($item->name.($k !== self::TAG_NOTAG ? ';'.$k : ''))=>$v]))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
@@ -62,12 +78,10 @@ class Entry extends Model
|
||||
{
|
||||
$key = $this->normalizeAttributeKey($key);
|
||||
|
||||
// @todo Silently ignore keys of language tags - we should work with them
|
||||
if (str_contains($key,';'))
|
||||
return TRUE;
|
||||
list($attribute,$tag) = $this->keytag($key);
|
||||
|
||||
return ((! array_key_exists($key,$this->original)) && (! $this->objects->has($key)))
|
||||
|| (! $this->getObject($key)->isDirty());
|
||||
return ((! array_key_exists($key,$this->original)) && (! $this->objects->has($attribute)))
|
||||
|| (! $this->getObject($attribute)->isDirty());
|
||||
}
|
||||
|
||||
public static function query(bool $noattrs=false): Builder
|
||||
@@ -82,24 +96,26 @@ class Entry extends Model
|
||||
|
||||
/**
|
||||
* As attribute values are updated, or new ones created, we need to mirror that
|
||||
* into our $objects
|
||||
* into our $objects. This is called when we $o->key = $value
|
||||
*
|
||||
* This function should update $this->attributes and correctly reflect changes in $this->objects
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute(string $key, mixed $value): static
|
||||
public function setAttribute(string $key,mixed $value): static
|
||||
{
|
||||
parent::setAttribute($key,$value);
|
||||
foreach ($value as $k => $v)
|
||||
parent::setAttribute($key.($k !== self::TAG_NOTAG ? ';'.$k : ''),$v);
|
||||
|
||||
$key = $this->normalizeAttributeKey($key);
|
||||
list($attribute,$tags) = $this->keytag($key);
|
||||
|
||||
if ((! $this->objects->get($key)) && $value) {
|
||||
$this->objects->put($key,Factory::create($key,$value));
|
||||
$o = $this->objects->get($attribute) ?: Factory::create($this->dn ?: '',$attribute,[],Arr::get($this->attributes,'objectclass',[]));
|
||||
$o->values = collect($value);
|
||||
|
||||
} elseif ($this->objects->get($key)) {
|
||||
$this->objects->get($key)->value = $this->attributes[$key];
|
||||
}
|
||||
$this->objects->put($key,$o);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -133,68 +149,105 @@ class Entry extends Model
|
||||
* Return a key to use for sorting
|
||||
*
|
||||
* @return string
|
||||
* @todo This should be the DN in reverse order
|
||||
*/
|
||||
public function getSortKeyAttribute(): string
|
||||
{
|
||||
return $this->getDn();
|
||||
return collect(explode(',',$this->getDn()))->reverse()->join(',');
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
public function addAttribute(string $key,mixed $value): void
|
||||
/**
|
||||
* Add an attribute to this entry, if the attribute already exists, then we'll add the value to the existing item.
|
||||
*
|
||||
* This is primarily used by LDIF imports, where attributes have multiple entries over multiple lines
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws AttributeException
|
||||
* @note Attributes added this way dont have objectclass information, and the Model::attributes are not populated
|
||||
*/
|
||||
public function addAttributeItem(string $key,mixed $value): void
|
||||
{
|
||||
// While $value is mixed, it can only be a string
|
||||
if (! is_string($value))
|
||||
throw new \Exception('value should be a string');
|
||||
|
||||
$key = $this->normalizeAttributeKey($key);
|
||||
$key = $this->normalizeAttributeKey(strtolower($key));
|
||||
|
||||
if (! config('server')->schema('attributetypes')->has($key))
|
||||
throw new AttributeException(sprintf('Schema doesnt have attribute [%s]',$key));
|
||||
// If the attribute name has tags
|
||||
list($attribute,$tag) = $this->keytag($key);
|
||||
|
||||
if ($x=$this->objects->get($key)) {
|
||||
$x->addValue($value);
|
||||
if (! config('server')->schema('attributetypes')->has($attribute))
|
||||
throw new AttributeException(sprintf('Schema doesnt have attribute [%s]',$attribute));
|
||||
|
||||
} else {
|
||||
$this->objects->put($key,Attribute\Factory::create($key,Arr::wrap($value)));
|
||||
$o = $this->objects->get($attribute) ?: Attribute\Factory::create($this->dn ?: '',$attribute,[]);
|
||||
$o->addValue($tag,[$value]);
|
||||
|
||||
$this->objects->put($attribute,$o);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all our attribute values into an array of Objects
|
||||
*
|
||||
* @param array $attributes
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAttributesAsObjects(): Collection
|
||||
private function getAttributesAsObjects(): Collection
|
||||
{
|
||||
$result = collect();
|
||||
$entry_oc = Arr::get($this->attributes,'objectclass',[]);
|
||||
|
||||
foreach ($this->attributes 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];
|
||||
foreach ($this->attributes as $attrtag => $values) {
|
||||
list($attribute,$tags) = $this->keytag($attrtag);
|
||||
|
||||
// If the attribute doesnt exist we'll create it
|
||||
$o = Arr::get($result,$attribute,Factory::create($attribute,[]));
|
||||
$o->setLangTag($matches[3],$value);
|
||||
$orig = Arr::get($this->original,$attrtag,[]);
|
||||
|
||||
} else {
|
||||
$o = Factory::create($attribute,$value);
|
||||
}
|
||||
// If the attribute doesnt exist we'll create it
|
||||
$o = Arr::get(
|
||||
$result,
|
||||
$attribute,
|
||||
Factory::create(
|
||||
$this->dn,
|
||||
$attribute,
|
||||
[$tags=>$orig],
|
||||
$entry_oc,
|
||||
));
|
||||
|
||||
if (! $result->has($attribute)) {
|
||||
// Set the rdn flag
|
||||
if (preg_match('/^'.$attribute.'=/i',$this->dn))
|
||||
$o->setRDN();
|
||||
$o->addValue($tags,$values);
|
||||
$o->addValueOld($tags,Arr::get($this->original,$attrtag));
|
||||
|
||||
// Store our original value to know if this attribute has changed
|
||||
$o->oldValues(Arr::get($this->original,$attribute,[]));
|
||||
|
||||
$result->put($attribute,$o);
|
||||
}
|
||||
$result->put($attribute,$o);
|
||||
}
|
||||
|
||||
$sort = collect(config('pla.attr_display_order',[]))->map(fn($item)=>strtolower($item));
|
||||
@@ -235,7 +288,7 @@ class Entry extends Model
|
||||
{
|
||||
$result = collect();
|
||||
|
||||
foreach ($this->objectclass as $oc)
|
||||
foreach ($this->getObject('objectclass')->values as $oc)
|
||||
$result = $result->merge(config('server')->schema('objectclasses',$oc)->attributes);
|
||||
|
||||
return $result;
|
||||
@@ -262,6 +315,38 @@ class Entry extends Model
|
||||
->filter(fn($item)=>$item->is_internal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify the language tags (RFC 3866) used by this entry
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getLangTags(): Collection
|
||||
{
|
||||
return $this->getObjects()
|
||||
->filter(fn($item)=>! $item->no_attr_tags)
|
||||
->map(fn($item)=>$item
|
||||
->values
|
||||
->keys()
|
||||
->filter(fn($item)=>preg_match(sprintf('/%s+;?/',self::TAG_CHARS_LANG),$item))
|
||||
->map(fn($item)=>preg_replace('/lang-/','',$item))
|
||||
)
|
||||
->filter(fn($item)=>$item->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Of all the items with lang tags, which ones have more than 1 lang tag
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getLangMultiTags(): Collection
|
||||
{
|
||||
return $this->getLangTags()
|
||||
->map(fn($item)=>$item->values()
|
||||
->map(fn($item)=>explode(';',$item))
|
||||
->filter(fn($item)=>count($item) > 1))
|
||||
->filter(fn($item)=>$item->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an attribute as an object
|
||||
*
|
||||
@@ -287,10 +372,36 @@ class Entry extends Model
|
||||
return $this->objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find other attribute tags used by this entry
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getOtherTags(): Collection
|
||||
{
|
||||
return $this->getObjects()
|
||||
->filter(fn($item)=>! $item->no_attr_tags)
|
||||
->map(fn($item)=>$item
|
||||
->values
|
||||
->keys()
|
||||
->filter(fn($item)=>
|
||||
$item && collect(explode(';',$item))->filter(
|
||||
fn($item)=>
|
||||
(! preg_match(sprintf('/^%s$/',self::TAG_NOTAG),$item))
|
||||
&& (! preg_match(sprintf('/^%s+$/',self::TAG_CHARS_LANG),$item))
|
||||
)
|
||||
->count())
|
||||
)
|
||||
->filter(fn($item)=>$item->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of attributes without any values
|
||||
*
|
||||
* @return Collection
|
||||
* @todo Dont show attributes that are not provided by an objectclass, make a new function to show those
|
||||
* This is for dynamic list items eg: labeledURI, which are not editable.
|
||||
* We can highlight those values that are as a result of a dynamic module
|
||||
*/
|
||||
public function getMissingAttributes(): Collection
|
||||
{
|
||||
@@ -300,8 +411,8 @@ class Entry extends Model
|
||||
|
||||
private function getRDNObject(): Attribute\RDN
|
||||
{
|
||||
$o = new Attribute\RDN('dn',['']);
|
||||
// @todo for an existing object, return the base.
|
||||
$o = new Attribute\RDN('','dn',['']);
|
||||
// @todo for an existing object, rdnbase would be null, so dynamically get it from the DN.
|
||||
$o->setBase($this->rdnbase);
|
||||
$o->setAttributes($this->getAvailableAttributes()->filter(fn($item)=>$item->required));
|
||||
|
||||
@@ -311,12 +422,22 @@ class Entry extends Model
|
||||
/**
|
||||
* Return this list of user attributes
|
||||
*
|
||||
* @param string $tag If null return all tags
|
||||
* @return Collection
|
||||
*/
|
||||
public function getVisibleAttributes(): Collection
|
||||
public function getVisibleAttributes(string $tag=''): Collection
|
||||
{
|
||||
return $this->objects
|
||||
->filter(fn($item)=>! $item->is_internal);
|
||||
static $cache = [];
|
||||
|
||||
if (! Arr::get($cache,$tag ?: '_all_')) {
|
||||
$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)=>(! $tag) || $ot->has($item->name_lc) || count($item->tagValues($tag)) > 0);
|
||||
}
|
||||
|
||||
return $cache[$tag ?: '_all_'];
|
||||
}
|
||||
|
||||
public function hasAttribute(int|string $key): bool
|
||||
@@ -325,36 +446,6 @@ class Entry extends Model
|
||||
->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
|
||||
*
|
||||
@@ -362,69 +453,97 @@ class Entry extends Model
|
||||
*/
|
||||
public function icon(): string
|
||||
{
|
||||
$objectclasses = array_map('strtolower',$this->objectclass);
|
||||
$objectclasses = $this->getObject('objectclass')
|
||||
->tagValues()
|
||||
->map(fn($item)=>strtolower($item));
|
||||
|
||||
// Return icon based upon objectClass value
|
||||
if (in_array('person',$objectclasses) ||
|
||||
in_array('organizationalperson',$objectclasses) ||
|
||||
in_array('inetorgperson',$objectclasses) ||
|
||||
in_array('account',$objectclasses) ||
|
||||
in_array('posixaccount',$objectclasses))
|
||||
|
||||
if ($objectclasses->intersect([
|
||||
'account',
|
||||
'inetorgperson',
|
||||
'organizationalperson',
|
||||
'person',
|
||||
'posixaccount',
|
||||
])->count())
|
||||
return 'fas fa-user';
|
||||
|
||||
elseif (in_array('organization',$objectclasses))
|
||||
elseif ($objectclasses->contains('organization'))
|
||||
return 'fas fa-university';
|
||||
|
||||
elseif (in_array('organizationalunit',$objectclasses))
|
||||
elseif ($objectclasses->contains('organizationalunit'))
|
||||
return 'fas fa-object-group';
|
||||
|
||||
elseif (in_array('posixgroup',$objectclasses) ||
|
||||
in_array('groupofnames',$objectclasses) ||
|
||||
in_array('groupofuniquenames',$objectclasses) ||
|
||||
in_array('group',$objectclasses))
|
||||
|
||||
elseif ($objectclasses->intersect([
|
||||
'posixgroup',
|
||||
'groupofnames',
|
||||
'groupofuniquenames',
|
||||
'group',
|
||||
])->count())
|
||||
return 'fas fa-users';
|
||||
|
||||
elseif (in_array('dcobject',$objectclasses) ||
|
||||
in_array('domainrelatedobject',$objectclasses) ||
|
||||
in_array('domain',$objectclasses) ||
|
||||
in_array('builtindomain',$objectclasses))
|
||||
|
||||
elseif ($objectclasses->intersect([
|
||||
'dcobject',
|
||||
'domainrelatedobject',
|
||||
'domain',
|
||||
'builtindomain',
|
||||
])->count())
|
||||
return 'fas fa-network-wired';
|
||||
|
||||
elseif (in_array('alias',$objectclasses))
|
||||
elseif ($objectclasses->contains('alias'))
|
||||
return 'fas fa-theater-masks';
|
||||
|
||||
elseif (in_array('country',$objectclasses))
|
||||
return sprintf('flag %s',strtolower(Arr::get($this->c,0)));
|
||||
elseif ($objectclasses->contains('country'))
|
||||
return sprintf('flag %s',strtolower(Arr::get($this->c ?: [],0)));
|
||||
|
||||
elseif (in_array('device',$objectclasses))
|
||||
elseif ($objectclasses->contains('device'))
|
||||
return 'fas fa-mobile-alt';
|
||||
|
||||
elseif (in_array('document',$objectclasses))
|
||||
elseif ($objectclasses->contains('document'))
|
||||
return 'fas fa-file-alt';
|
||||
|
||||
elseif (in_array('iphost',$objectclasses))
|
||||
elseif ($objectclasses->contains('iphost'))
|
||||
return 'fas fa-wifi';
|
||||
|
||||
elseif (in_array('room',$objectclasses))
|
||||
elseif ($objectclasses->contains('room'))
|
||||
return 'fas fa-door-open';
|
||||
|
||||
elseif (in_array('server',$objectclasses))
|
||||
elseif ($objectclasses->contains('server'))
|
||||
return 'fas fa-server';
|
||||
|
||||
elseif (in_array('openldaprootdse',$objectclasses))
|
||||
elseif ($objectclasses->contains('openldaprootdse'))
|
||||
return 'fas fa-info';
|
||||
|
||||
// Default
|
||||
return 'fa-fw fas fa-cog';
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an LDAP attribute, this will return the attribute name and the tag
|
||||
* eg: description;lang-cn will return [description,lang-cn]
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
private function keytag(string $key): array
|
||||
{
|
||||
$matches = [];
|
||||
if (preg_match(sprintf('/^([%s]+);+([%s;]+)/',self::TAG_CHARS,self::TAG_CHARS),$key,$matches)) {
|
||||
$attribute = $matches[1];
|
||||
$tags = $matches[2];
|
||||
|
||||
} else {
|
||||
$attribute = $key;
|
||||
$tags = self::TAG_NOTAG;
|
||||
}
|
||||
|
||||
return [$attribute,$tags];
|
||||
}
|
||||
|
||||
/**
|
||||
* Dont convert our $this->attributes to $this->objects when creating a new Entry::class
|
||||
*
|
||||
* @return $this
|
||||
* @deprecated
|
||||
*/
|
||||
public function noObjectAttributes(): static
|
||||
{
|
||||
|
@@ -14,7 +14,7 @@ use LdapRecord\Models\Model as LdapRecord;
|
||||
*/
|
||||
class LoginObjectclassRule implements Rule
|
||||
{
|
||||
public function passes(LdapRecord $user, Eloquent $model = null): bool
|
||||
public function passes(LdapRecord $user,?Eloquent $model=NULL): bool
|
||||
{
|
||||
if ($x=config('pla.login.objectclass')) {
|
||||
return count(array_intersect($user->objectclass,$x));
|
||||
|
@@ -20,7 +20,7 @@ class HasStructuralObjectClass implements ValidationRule
|
||||
*/
|
||||
public function validate(string $attribute,mixed $value,Closure $fail): void
|
||||
{
|
||||
foreach ($value as $item)
|
||||
foreach (collect($value)->dot() as $item)
|
||||
if ($item && config('server')->schema('objectclasses',$item)->isStructural())
|
||||
return;
|
||||
|
||||
|
@@ -11,8 +11,8 @@ trait MD5Updates
|
||||
{
|
||||
public function isDirty(): bool
|
||||
{
|
||||
foreach ($this->values->diff($this->oldValues) as $key => $value)
|
||||
if (md5(Arr::get($this->oldValues,$key)) !== $value)
|
||||
foreach ($this->values_old->dot()->keys()->merge($this->values->dot()->keys())->unique() as $dotkey)
|
||||
if (md5(Arr::get($this->values_old->dot(),$dotkey)) !== Arr::get($this->values->dot(),$dotkey))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
|
@@ -2,9 +2,12 @@
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
use App\Classes\LDAP\Attribute as LDAPAttribute;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
class Attribute extends Component
|
||||
{
|
||||
@@ -12,30 +15,32 @@ class Attribute extends Component
|
||||
public bool $edit;
|
||||
public bool $new;
|
||||
public bool $old;
|
||||
public ?string $na;
|
||||
public string $langtag;
|
||||
public ?string $na; // Text to render if the LDAPAttribute is null
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(?LDAPAttribute $o,bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,?string $na=NULL)
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(?LDAPAttribute $o,bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,string $langtag=Entry::TAG_NOTAG,?string $na=NULL)
|
||||
{
|
||||
$this->o = $o;
|
||||
$this->edit = $edit;
|
||||
$this->old = $old;
|
||||
$this->new = $new;
|
||||
$this->langtag = $langtag;
|
||||
$this->na = $na;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View|\Closure|string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*
|
||||
* @return View|string
|
||||
*/
|
||||
public function render(): View|string
|
||||
{
|
||||
return $this->o
|
||||
? $this->o
|
||||
->render($this->edit,$this->old,$this->new)
|
||||
->render(edit: $this->edit,old: $this->old,new: $this->new)
|
||||
: $this->na;
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,37 +2,39 @@
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
use App\Classes\LDAP\Attribute as LDAPAttribute;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
class AttributeType extends Component
|
||||
{
|
||||
public Collection $oc;
|
||||
public LDAPAttribute $o;
|
||||
public bool $new;
|
||||
private LDAPAttribute $o;
|
||||
private bool $new;
|
||||
private bool $edit;
|
||||
private string $langtag;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(LDAPAttribute $o,bool $new=FALSE,?Collection $oc=NULL)
|
||||
public function __construct(LDAPAttribute $o,bool $new=FALSE,bool $edit=FALSE,string $langtag=Entry::TAG_NOTAG)
|
||||
{
|
||||
$this->o = $o;
|
||||
$this->oc = $oc;
|
||||
$this->new = $new;
|
||||
$this->edit = $edit;
|
||||
$this->langtag = $langtag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
public function render(): View
|
||||
{
|
||||
return view('components.attribute-type')
|
||||
->with('o',$this->o)
|
||||
->with('oc',$this->oc)
|
||||
->with('new',$this->new);
|
||||
->with('new',$this->new)
|
||||
->with('edit',$this->edit)
|
||||
->with('langtag',$this->langtag);
|
||||
}
|
||||
}
|
@@ -30,6 +30,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
|
||||
$middleware->trustProxies(at: [
|
||||
'10.0.0.0/8',
|
||||
'127.0.0.0/8',
|
||||
'172.16.0.0/12',
|
||||
'192.168.0.0/12',
|
||||
]);
|
||||
|
@@ -7,6 +7,7 @@
|
||||
"require": {
|
||||
"ext-fileinfo": "*",
|
||||
"ext-ldap": "*",
|
||||
"ext-openssl": "*",
|
||||
"php": "^8.4",
|
||||
"directorytree/ldaprecord-laravel": "^3.0",
|
||||
"laravel/framework": "^11.9",
|
||||
|
38
composer.lock
generated
38
composer.lock
generated
@@ -1199,16 +1199,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v11.44.1",
|
||||
"version": "v11.44.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "0883d4175f4e2b5c299e7087ad3c74f2ce195c6d"
|
||||
"reference": "f85216c82cbd38b66d67ebd20ea762cb3751a4b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/0883d4175f4e2b5c299e7087ad3c74f2ce195c6d",
|
||||
"reference": "0883d4175f4e2b5c299e7087ad3c74f2ce195c6d",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/f85216c82cbd38b66d67ebd20ea762cb3751a4b4",
|
||||
"reference": "f85216c82cbd38b66d67ebd20ea762cb3751a4b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1410,7 +1410,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2025-03-05T15:34:10+00:00"
|
||||
"time": "2025-03-12T14:34:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
@@ -6939,16 +6939,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "11.5.11",
|
||||
"version": "11.5.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "3946ac38410be7440186c6e74584f31b15107fc7"
|
||||
"reference": "d42785840519401ed2113292263795eb4c0f95da"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3946ac38410be7440186c6e74584f31b15107fc7",
|
||||
"reference": "3946ac38410be7440186c6e74584f31b15107fc7",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d42785840519401ed2113292263795eb4c0f95da",
|
||||
"reference": "d42785840519401ed2113292263795eb4c0f95da",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -6969,7 +6969,7 @@
|
||||
"phpunit/php-timer": "^7.0.1",
|
||||
"sebastian/cli-parser": "^3.0.2",
|
||||
"sebastian/code-unit": "^3.0.2",
|
||||
"sebastian/comparator": "^6.3.0",
|
||||
"sebastian/comparator": "^6.3.1",
|
||||
"sebastian/diff": "^6.0.2",
|
||||
"sebastian/environment": "^7.2.0",
|
||||
"sebastian/exporter": "^6.3.0",
|
||||
@@ -7020,7 +7020,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.11"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.12"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7036,7 +7036,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-05T07:36:02+00:00"
|
||||
"time": "2025-03-07T07:31:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@@ -7210,16 +7210,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/comparator",
|
||||
"version": "6.3.0",
|
||||
"version": "6.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/comparator.git",
|
||||
"reference": "d4e47a769525c4dd38cea90e5dcd435ddbbc7115"
|
||||
"reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/d4e47a769525c4dd38cea90e5dcd435ddbbc7115",
|
||||
"reference": "d4e47a769525c4dd38cea90e5dcd435ddbbc7115",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/24b8fbc2c8e201bb1308e7b05148d6ab393b6959",
|
||||
"reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7238,7 +7238,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "6.2-dev"
|
||||
"dev-main": "6.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -7278,7 +7278,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/comparator/issues",
|
||||
"security": "https://github.com/sebastianbergmann/comparator/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/comparator/tree/6.3.0"
|
||||
"source": "https://github.com/sebastianbergmann/comparator/tree/6.3.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7286,7 +7286,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-06T10:28:19+00:00"
|
||||
"time": "2025-03-07T06:57:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/complexity",
|
||||
|
@@ -122,54 +122,47 @@ return [
|
||||
*/
|
||||
'validation' => [
|
||||
'objectclass' => [
|
||||
'objectclass'=>[
|
||||
'required',
|
||||
'array',
|
||||
'min:1',
|
||||
'objectclass.*'=>[
|
||||
new HasStructuralObjectClass,
|
||||
]
|
||||
],
|
||||
'gidnumber' => [
|
||||
'gidnumber'=> [
|
||||
'gidnumber.*'=> [
|
||||
'sometimes',
|
||||
'array',
|
||||
'max:1'
|
||||
],
|
||||
'gidnumber.*' => [
|
||||
'gidnumber.*.*' => [
|
||||
'nullable',
|
||||
'integer',
|
||||
'max:65535'
|
||||
]
|
||||
],
|
||||
'mail' => [
|
||||
'mail'=>[
|
||||
'mail.*'=>[
|
||||
'sometimes',
|
||||
'array',
|
||||
'min:1'
|
||||
],
|
||||
'mail.*' => [
|
||||
'mail.*.*' => [
|
||||
'nullable',
|
||||
'email'
|
||||
]
|
||||
],
|
||||
'userpassword' => [
|
||||
'userpassword' => [
|
||||
'userpassword.*' => [
|
||||
'sometimes',
|
||||
'array',
|
||||
'min:1'
|
||||
],
|
||||
'userpassword.*' => [
|
||||
'userpassword.*.*' => [
|
||||
'nullable',
|
||||
'min:8'
|
||||
]
|
||||
],
|
||||
'uidnumber' => [
|
||||
'uidnumber' => [
|
||||
'uidnumber.*' => [
|
||||
'sometimes',
|
||||
'array',
|
||||
'max:1'
|
||||
],
|
||||
'uidnumber.*' => [
|
||||
'uidnumber.*.*' => [
|
||||
'nullable',
|
||||
'integer',
|
||||
'max:65535'
|
||||
|
@@ -54,6 +54,7 @@
|
||||
1.3.6.1.4.1.42.2.27.8.5.1:passwordPolicyRequest
|
||||
1.3.6.1.4.1.42.2.27.9.5.2:GetEffectiveRights control::May be used to determine what operations a given user may perform on a specified entry.
|
||||
1.3.6.1.4.1.1466.101.119.1:Dynamic Directory Services Refresh Request:RFC 2589
|
||||
1.3.6.1.4.1.1466.115.121.1.25:"guide" syntax-name:RFC 4517
|
||||
1.3.6.1.4.1.1466.20036:LDAP_NOTICE_OF_DISCONNECTION
|
||||
1.3.6.1.4.1.1466.20037:Transport Layer Security Extension:RFC 2830:This operation provides for TLS establishment in an LDAP association and is defined in terms of an LDAP extended request.
|
||||
1.3.6.1.4.1.1466.29539.1:LDAP_CONTROL_ATTR_SIZELIMIT
|
||||
|
162
package-lock.json
generated
162
package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"animate-sass": "^0.8.2",
|
||||
"axios": "^1.3.4",
|
||||
"bootstrap": "^5.2.3",
|
||||
"bootstrap-icons": "^1.11.3",
|
||||
"jquery": "^3.6.3",
|
||||
"jquery-ui": "^1.13.2",
|
||||
"jquery.fancytree": "^2.38.3",
|
||||
@@ -61,21 +62,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz",
|
||||
"integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==",
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
|
||||
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/generator": "^7.26.9",
|
||||
"@babel/generator": "^7.26.10",
|
||||
"@babel/helper-compilation-targets": "^7.26.5",
|
||||
"@babel/helper-module-transforms": "^7.26.0",
|
||||
"@babel/helpers": "^7.26.9",
|
||||
"@babel/parser": "^7.26.9",
|
||||
"@babel/helpers": "^7.26.10",
|
||||
"@babel/parser": "^7.26.10",
|
||||
"@babel/template": "^7.26.9",
|
||||
"@babel/traverse": "^7.26.9",
|
||||
"@babel/types": "^7.26.9",
|
||||
"@babel/traverse": "^7.26.10",
|
||||
"@babel/types": "^7.26.10",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.2",
|
||||
@@ -100,13 +101,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz",
|
||||
"integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==",
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz",
|
||||
"integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.26.9",
|
||||
"@babel/types": "^7.26.9",
|
||||
"@babel/parser": "^7.26.10",
|
||||
"@babel/types": "^7.26.10",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jsesc": "^3.0.2"
|
||||
@@ -377,25 +378,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz",
|
||||
"integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==",
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz",
|
||||
"integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.26.9",
|
||||
"@babel/types": "^7.26.9"
|
||||
"@babel/types": "^7.26.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz",
|
||||
"integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==",
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz",
|
||||
"integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.9"
|
||||
"@babel/types": "^7.26.10"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -1234,15 +1235,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-runtime": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.9.tgz",
|
||||
"integrity": "sha512-Jf+8y9wXQbbxvVYTM8gO5oEF2POdNji0NMltEkG7FtmzD9PVz7/lxpqSdTvwsjTMU5HIHuDVNf2SOxLkWi+wPQ==",
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz",
|
||||
"integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.25.9",
|
||||
"@babel/helper-plugin-utils": "^7.26.5",
|
||||
"babel-plugin-polyfill-corejs2": "^0.4.10",
|
||||
"babel-plugin-polyfill-corejs3": "^0.10.6",
|
||||
"babel-plugin-polyfill-corejs3": "^0.11.0",
|
||||
"babel-plugin-polyfill-regenerator": "^0.6.1",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
@@ -1484,19 +1485,6 @@
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz",
|
||||
"integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.3",
|
||||
"core-js-compat": "^3.40.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/preset-env/node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
@@ -1521,9 +1509,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz",
|
||||
"integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==",
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
|
||||
"integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
@@ -1547,16 +1535,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz",
|
||||
"integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==",
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz",
|
||||
"integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/generator": "^7.26.9",
|
||||
"@babel/parser": "^7.26.9",
|
||||
"@babel/generator": "^7.26.10",
|
||||
"@babel/parser": "^7.26.10",
|
||||
"@babel/template": "^7.26.9",
|
||||
"@babel/types": "^7.26.9",
|
||||
"@babel/types": "^7.26.10",
|
||||
"debug": "^4.3.1",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -1565,9 +1553,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.26.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
|
||||
"integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
|
||||
"version": "7.26.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz",
|
||||
"integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.9",
|
||||
@@ -2260,9 +2248,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.13.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz",
|
||||
"integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==",
|
||||
"version": "22.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
@@ -2779,9 +2767,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.20",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
||||
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
|
||||
"version": "10.4.21",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
||||
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -2798,11 +2786,11 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.23.3",
|
||||
"caniuse-lite": "^1.0.30001646",
|
||||
"browserslist": "^4.24.4",
|
||||
"caniuse-lite": "^1.0.30001702",
|
||||
"fraction.js": "^4.3.7",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.1",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -2816,9 +2804,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz",
|
||||
"integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz",
|
||||
"integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
@@ -2869,13 +2857,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-polyfill-corejs3": {
|
||||
"version": "0.10.6",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
|
||||
"integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz",
|
||||
"integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.2",
|
||||
"core-js-compat": "^3.38.0"
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.3",
|
||||
"core-js-compat": "^3.40.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
|
||||
@@ -3053,6 +3041,22 @@
|
||||
"@popperjs/core": "^2.11.8"
|
||||
}
|
||||
},
|
||||
"node_modules/bootstrap-icons": {
|
||||
"version": "1.11.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
|
||||
"integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/twbs"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -3311,9 +3315,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001702",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz",
|
||||
"integrity": "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==",
|
||||
"version": "1.0.30001703",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz",
|
||||
"integrity": "sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -4324,9 +4328,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.112",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.112.tgz",
|
||||
"integrity": "sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA==",
|
||||
"version": "1.5.115",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.115.tgz",
|
||||
"integrity": "sha512-MN1nahVHAQMOz6dz6bNZ7apgqc9InZy7Ja4DBEVCTdeiUcegbyOYE9bi/f2Z/z6ZxLi0RxLpyJ3EGe+4h3w73A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/elliptic": {
|
||||
@@ -6545,9 +6549,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"version": "3.3.9",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz",
|
||||
"integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -9027,9 +9031,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin": {
|
||||
"version": "5.3.13",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.13.tgz",
|
||||
"integrity": "sha512-JG3pBixF6kx2o0Yfz2K6pqh72DpwTI08nooHd06tcj5WyIt5SsSiUYqRT+kemrGUNSuSzVhwfZ28aO8gogajNQ==",
|
||||
"version": "5.3.14",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz",
|
||||
"integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
|
@@ -15,6 +15,7 @@
|
||||
"animate-sass": "^0.8.2",
|
||||
"axios": "^1.3.4",
|
||||
"bootstrap": "^5.2.3",
|
||||
"bootstrap-icons": "^1.11.3",
|
||||
"jquery": "^3.6.3",
|
||||
"jquery-ui": "^1.13.2",
|
||||
"jquery.fancytree": "^2.38.3",
|
||||
|
@@ -1 +1 @@
|
||||
v2.0.1-rel
|
||||
v2.1.0-dev
|
||||
|
9
public/css/custom.css
vendored
9
public/css/custom.css
vendored
@@ -1,8 +1,8 @@
|
||||
/** ensure our userpassword has select is next to the password input */
|
||||
div#userPassword .select2-container--bootstrap-5 .select2-selection {
|
||||
attribute#userPassword .select2-container--bootstrap-5 .select2-selection {
|
||||
font-size: inherit;
|
||||
width: 9em;
|
||||
border: #444054 1px solid;
|
||||
border: var(--bs-gray-500) 1px solid;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ div#userPassword .select2-container--bootstrap-5 .select2-selection {
|
||||
border-top-right-radius: unset;
|
||||
}
|
||||
|
||||
div#objectClass .input-group-end:not(input.form-control) {
|
||||
attribute#objectClass .input-group-end:not(input.form-control) {
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
top: 0.5em;
|
||||
@@ -30,7 +30,10 @@ input.form-control.input-group-end {
|
||||
|
||||
.custom-tooltip-danger {
|
||||
--bs-tooltip-bg: var(--bs-danger);
|
||||
}
|
||||
|
||||
.custom-tooltip {
|
||||
--bs-tooltip-bg: var(--bs-gray-900);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
|
3
resources/sass/app.scss
vendored
3
resources/sass/app.scss
vendored
@@ -7,3 +7,6 @@
|
||||
// Select2
|
||||
@import "select2/dist/css/select2";
|
||||
@import "select2-bootstrap-5-theme/dist/select2-bootstrap-5-theme";
|
||||
|
||||
// Bootstrap icons
|
||||
@import "bootstrap-icons"
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<div class="h5 modal-title text-center">
|
||||
<h4 class="mt-2">
|
||||
<div class="app-logo mx-auto mb-3"><img class="w-75" src="{{ url('images/logo-h-lg.png') }}"></div>
|
||||
<small>@lang('Sign in to') <strong>{{ config('server')->name }}</strong></small>
|
||||
<small>@lang('Sign in to') <strong>{{ $server->name }}</strong></small>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
|
@@ -14,54 +14,7 @@
|
||||
</div>
|
||||
|
||||
<div class="page-title-actions">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="action-buttons float-end">
|
||||
<ul class="nav">
|
||||
@if(isset($page_actions) && $page_actions->contains('export'))
|
||||
<li>
|
||||
<span data-bs-toggle="modal" data-bs-target="#entry_export-modal">
|
||||
<button class="btn btn-outline-dark p-1 m-1" data-bs-toggle="tooltip" data-bs-placement="bottom" title="@lang('Export')"><i class="fas fa-fw fa-download fs-5"></i></button>
|
||||
</span>
|
||||
</li>
|
||||
@endif
|
||||
@if(isset($page_actions) && $page_actions->contains('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')"><i class="fas fa-fw fa-copy fs-5"></i></button>
|
||||
</li>
|
||||
@endif
|
||||
@if((isset($page_actions) && $page_actions->contains('edit')) || old())
|
||||
<li>
|
||||
<button class="btn btn-outline-dark p-1 m-1" id="entry-edit" data-bs-toggle="tooltip" data-bs-placement="bottom" title="@lang('Edit Entry')"><i class="fas fa-fw fa-edit fs-5"></i></button>
|
||||
</li>
|
||||
@endif
|
||||
<!-- @todo Dont offer the delete button for an entry with children -->
|
||||
@if(isset($page_actions) && $page_actions->contains('delete'))
|
||||
<li>
|
||||
<span id="entry-delete" data-bs-toggle="modal" data-bs-target="#page-modal">
|
||||
<button class="btn btn-outline-danger p-1 m-1" data-bs-custom-class="custom-tooltip-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="@lang('Delete Entry')"><i class="fas fa-fw fa-trash-can fs-5"></i></button>
|
||||
</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@yield('page_actions')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('page-scripts')
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('button[id=entry-edit]').on('click',function(item) {
|
||||
item.preventDefault();
|
||||
|
||||
if ($(this).hasClass('btn-dark'))
|
||||
return;
|
||||
|
||||
editmode();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@append
|
||||
</div>
|
@@ -32,7 +32,7 @@
|
||||
<div class="scrollbar-sidebar">
|
||||
<div class="app-sidebar__inner">
|
||||
<ul class="vertical-nav-menu">
|
||||
<li class="app-sidebar__heading">{{ config('server')->name }}</li>
|
||||
<li class="app-sidebar__heading">{{ $server->name }}</li>
|
||||
<li>
|
||||
<i id="treeicon" class="metismenu-icon fa-fw fas fa-sitemap"></i>
|
||||
<span class="f16" id="tree"></span>
|
||||
|
@@ -0,0 +1,9 @@
|
||||
<div class="alert alert-danger p-0" style="font-size: .80em;">
|
||||
<table class="table table-borderless table-danger p-0 m-0">
|
||||
<tr>
|
||||
<td class="align-top" style="width: 5%;"><i class="fas fa-fw fa-2x fa-exclamation-triangle"></i></td>
|
||||
<td>Unable to display this attribute as it has attribute tags [<strong>{!! $tags->join('</strong>, <strong>') !!}</strong>].<br>
|
||||
You can manage it with an LDIF import.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
@@ -15,6 +15,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-attribute :o="$o" :edit="true" :new="$new ?? FALSE"/>
|
||||
<x-attribute :o="$o" :edit="$edit" :new="$new" :langtag="$langtag"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@yield($o->name_lc.'-scripts')
|
@@ -1,19 +1,21 @@
|
||||
<!-- $o=Attribute::class -->
|
||||
<x-attribute.layout :edit="$edit ?? FALSE" :new="$new ?? FALSE" :o="$o">
|
||||
@foreach(old($o->name_lc,($new ?? FALSE) ? [NULL] : $o->values) as $value)
|
||||
@if (($edit ?? FALSE) && ! $o->is_rdn)
|
||||
<div class="input-group has-validation">
|
||||
<input type="text" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$loop->index)),'mb-1','border-focus'=>($o->values->contains($value))]) name="{{ $o->name_lc }}[]" value="{{ $value }}" placeholder="{{ ! is_null($x=Arr::get($o->values,$loop->index)) ? $x : '['.__('NEW').']' }}" @readonly(! ($new ?? FALSE))>
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o">
|
||||
<div class="col-12">
|
||||
@foreach(Arr::get(old($o->name_lc,[$langtag=>$new ? [NULL] : $o->tagValues($langtag)]),$langtag,[]) as $key => $value)
|
||||
@if($edit && (! $o->is_rdn))
|
||||
<div class="input-group has-validation">
|
||||
<input type="text" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)),'mb-1','border-focus'=>! ($tv=$o->tagValuesOld($langtag))->contains($value)]) name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ $value }}" placeholder="{{ ! is_null($x=$tv->get($loop->index)) ? $x : '['.__('NEW').']' }}" @readonly(! $new) @disabled($o->isDynamic())>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e)
|
||||
{{ join('|',$e) }}
|
||||
@endif
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e)
|
||||
{{ join('|',$e) }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@else
|
||||
{{ $value }}
|
||||
@endif
|
||||
@endforeach
|
||||
@else
|
||||
<input type="text" class="form-control mb-1" value="{{ $value }}" disabled>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</x-attribute.layout>
|
@@ -1,20 +1,20 @@
|
||||
<!-- @todo We are not handling redirect backs yet with updated photos -->
|
||||
<!-- $o=Binary\JpegPhoto::class -->
|
||||
<x-attribute.layout :edit="$edit" :new="false" :o="$o">
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o" :langtag="$langtag">
|
||||
<table class="table table-borderless p-0 m-0">
|
||||
@foreach (($old ? $o->old_values : $o->values) as $value)
|
||||
@foreach($o->tagValuesOld() as $key => $value)
|
||||
<tr>
|
||||
@switch ($x=$f->buffer($value,FILEINFO_MIME_TYPE))
|
||||
@switch($x=$f->buffer($value,FILEINFO_MIME_TYPE))
|
||||
@case('image/jpeg')
|
||||
@default
|
||||
<td>
|
||||
<input type="hidden" name="{{ $o->name_lc }}[]" value="{{ md5($value) }}">
|
||||
<img @class(['border','rounded','p-2','m-0','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$loop->index))]) src="data:{{ $x }};base64, {{ base64_encode($value) }}" />
|
||||
<input type="hidden" name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ md5($value) }}">
|
||||
<img alt="{{ $o->dn }}" @class(['border','rounded','p-2','m-0','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index))]) src="data:{{ $x }};base64, {{ base64_encode($value) }}" />
|
||||
|
||||
@if ($edit)
|
||||
@if($edit)
|
||||
<br>
|
||||
<!-- @todo TO IMPLEMENT -->
|
||||
<span class="btn btn-sm btn-danger deletable d-none mt-3"><i class="fas fa-trash-alt"></i> @lang('Delete')</span>
|
||||
<button class="btn btn-sm btn-danger deletable d-none mt-3" disabled><i class="fas fa-trash-alt"></i> @lang('Delete')</button>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<!-- $o=Internal\Timestamp::class -->
|
||||
@foreach (old($o->name_lc,$o->values) as $value)
|
||||
<!-- $o=Internal::class -->
|
||||
@foreach(old($o->name_lc,$o->values) as $value)
|
||||
@if($loop->index)<br>@endif
|
||||
{{ $value }}
|
||||
@endforeach
|
@@ -1,5 +1,5 @@
|
||||
<!-- $o=Internal\Timestamp::class -->
|
||||
@foreach (old($o->name_lc,$o->values) as $value)
|
||||
@foreach(old($o->name_lc,$o->values) as $value)
|
||||
@if($loop->index)<br>@endif
|
||||
{{ \Carbon\Carbon::createFromTimestamp(strtotime($value))->format(config('pla.datetime_format','Y-m-d H:i:s')) }}
|
||||
@endforeach
|
@@ -0,0 +1,2 @@
|
||||
<!-- $o=NoAttrTags/Generic::class -->
|
||||
@include('components.form.disabled.datetime')
|
@@ -0,0 +1,2 @@
|
||||
<!-- $o=NoAttrTags/Generic::class -->
|
||||
@include('components.form.disabled.datetime')
|
@@ -0,0 +1,2 @@
|
||||
<!-- $o=NoAttrTags/Generic::class -->
|
||||
@include('components.form.disabled.datetime')
|
@@ -0,0 +1,2 @@
|
||||
<!-- $o=NoAttrTags/Generic::class -->
|
||||
@include('components.form.disabled.input')
|
@@ -0,0 +1,2 @@
|
||||
<!-- $o=NoAttrTags/Generic::class -->
|
||||
@include('components.form.disabled.datetime')
|
@@ -0,0 +1,19 @@
|
||||
<!-- @todo We are not handling redirect backs yet with updated passwords -->
|
||||
<!-- $o=KrbPrincipleKey::class -->
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o" :langtag="$langtag">
|
||||
@foreach($o->tagValuesOld($langtag) as $key => $value)
|
||||
@if($edit)
|
||||
<div class="input-group has-validation mb-3">
|
||||
<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)]) name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ md5($value) }}" @readonly(true)>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e)
|
||||
{{ join('|',$e) }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
{{ $o->render_item_old($langtag.'.'.$key) }}
|
||||
@endif
|
||||
@endforeach
|
||||
</x-attribute.layout>
|
109
resources/views/components/attribute/krbticketflags.blade.php
Normal file
109
resources/views/components/attribute/krbticketflags.blade.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<!-- $o=KrbTicketFlags::class -->
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o">
|
||||
@foreach(Arr::get(old($o->name_lc,[$langtag=>$o->tagValues($langtag)]),$langtag,[]) as $key => $value)
|
||||
@if($edit)
|
||||
<div id="32"></div>
|
||||
<div id="16"></div>
|
||||
|
||||
<div class="input-group has-validation mb-3">
|
||||
<input type="hidden" name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ $value }}" @readonly(true)>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index))
|
||||
{{ join('|',$e) }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
{{ $o->render_item_old($langtag.'.'.$key) }}
|
||||
@endif
|
||||
@endforeach
|
||||
</x-attribute.layout>
|
||||
|
||||
@section($o->name_lc.'-scripts')
|
||||
<script type="text/javascript">
|
||||
var value = {{ $value ?? 0 }};
|
||||
var label = {!! $helper !!};
|
||||
|
||||
function tooltip(bit) {
|
||||
if (bit === undefined)
|
||||
return;
|
||||
|
||||
return label[bit] ? label[bit] : 'Bit '+bit;
|
||||
}
|
||||
|
||||
function binary(s=31,e=0) {
|
||||
var result = '';
|
||||
|
||||
for (let x=s;x>=e;x--) {
|
||||
var bit = (value&Math.pow(2,x));
|
||||
|
||||
result += '<i id="b'+x+'" style="margin-left:-1px;" class="fs-4 bi bi-'+(bit ? '1' : '0')+'-square'+(bit ? '-fill' : '')+'" data-bs-toggle="tooltip" data-bs-placement="bottom" title="'+tooltip(x)+'"></i>';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function krbticketflags() {
|
||||
$('div#32').append(binary(31,16));
|
||||
$('div#16').append(binary(15,0));
|
||||
|
||||
$('attribute#krbTicketFlags').find('i')
|
||||
.on('click',function() {
|
||||
var item = $(this);
|
||||
if ($('form#dn-edit').attr('readonly'))
|
||||
return;
|
||||
|
||||
var key = Number(item.attr('id').substring(1));
|
||||
|
||||
if (item.data('old') === undefined)
|
||||
item.data('old',null);
|
||||
|
||||
item.toggleClass('text-success');
|
||||
|
||||
// has the item changed?
|
||||
if (item.data('old') === null) {
|
||||
// It was set to 1
|
||||
if (item.hasClass('bi-1-square-fill')) {
|
||||
item.data('old',1);
|
||||
item.removeClass('bi-1-square-fill').addClass('bi-0-square-fill');
|
||||
|
||||
value -= Math.pow(2,key);
|
||||
|
||||
// It was set to 0
|
||||
} else if (item.hasClass('bi-0-square')) {
|
||||
item.data('old',0);
|
||||
item.removeClass('bi-0-square').addClass('bi-1-square-fill');
|
||||
|
||||
value += Math.pow(2,key);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (item.data('old') === 0) {
|
||||
item.removeClass('bi-1-square-fill').addClass('bi-0-square');
|
||||
value -= Math.pow(2,key);
|
||||
|
||||
} else {
|
||||
item.removeClass('bi-0-square-fill').addClass('bi-1-square-fill');
|
||||
value += Math.pow(2,key);
|
||||
}
|
||||
|
||||
item.data('old',null);
|
||||
}
|
||||
|
||||
$('attribute#krbTicketFlags').find('input').val(value);
|
||||
});
|
||||
}
|
||||
|
||||
// When returning to a Entry after an update, jquery hasnt loaded yet, so make sure we defer this to after the page has run
|
||||
if (window.$ === undefined) {
|
||||
document.addEventListener('DOMContentLoaded',() => krbticketflags());
|
||||
|
||||
} else {
|
||||
krbticketflags();
|
||||
|
||||
$('attribute#krbTicketFlags').find('i')
|
||||
.tooltip();
|
||||
}
|
||||
</script>
|
||||
@endsection
|
@@ -1,10 +1,12 @@
|
||||
<div class="row pt-2">
|
||||
<div @class(['col-1','d-none'=>(! $edit)])></div>
|
||||
<div class="col-10 p-2">
|
||||
<div id="{{ $o->name }}">
|
||||
<div @class(['col-1','d-none'=>(! $edit) && (! ($detail ?? false))])></div>
|
||||
<div class="col-10">
|
||||
<attribute id="{{ $o->name }}">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</attribute>
|
||||
|
||||
<x-attribute.widget.options :o="$o" :edit="$edit" :new="$new"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@yield($o->name_lc.'-scripts')
|
@@ -1,12 +1,12 @@
|
||||
<!-- $o=Attribute::class -->
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o">
|
||||
@foreach(old($o->name_lc,$o->values) as $value)
|
||||
@if ($edit)
|
||||
<x-attribute.widget.objectclass :o="$o" :edit="$edit" :new="$new" :loop="$loop" :value="$value"/>
|
||||
<!-- $o=Attribute/ObjectClass::class -->
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o" :langtag="$langtag">
|
||||
@foreach(Arr::get(old($o->name_lc,[$langtag=>$new ? [NULL] : $o->tagValues($langtag)]),$langtag,[]) as $key => $value)
|
||||
@if($edit)
|
||||
<x-attribute.widget.objectclass :o="$o" :edit="$edit" :new="$new" :loop="$loop" :value="$value" :langtag="$langtag"/>
|
||||
@else
|
||||
{{ $value }}
|
||||
{{ $o->render_item_old($key) }}
|
||||
@if ($o->isStructural($value))
|
||||
<input type="hidden" name="{{ $o->name_lc }}[]" value="{{ $value }}">
|
||||
<input type="hidden" name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ $value }}">
|
||||
<span class="float-end">@lang('structural')</span>
|
||||
@endif
|
||||
<br>
|
||||
|
@@ -1,10 +1,11 @@
|
||||
<!-- @todo We are not handling redirect backs yet with updated passwords -->
|
||||
<!-- $o=Password::class -->
|
||||
<x-attribute.layout :edit="$edit ?? FALSE" :new="$new ?? FALSE" :o="$o">
|
||||
@foreach($o->values as $value)
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o" :langtag="$langtag">
|
||||
@foreach($o->tagValuesOld($langtag) as $key => $value)
|
||||
@if($edit)
|
||||
<div class="input-group has-validation mb-3">
|
||||
<x-form.select id="userpassword_hash_{{$loop->index}}" name="userpassword_hash[]" :value="$o->hash($value)->id()" :options="$helpers" allowclear="false" :disabled="true"/>
|
||||
<input type="password" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$loop->index)),'mb-1','border-focus'=>$o->values->contains($value)]) name="{{ $o->name_lc }}[]" value="{{ md5($value) }}" @readonly(true)>
|
||||
<x-form.select id="userpassword_hash_{{$loop->index}}" name="userpassword_hash[{{ $langtag }}][]" :value="$o->hash($value)->id()" :options="$helpers" allowclear="false" :disabled="true"/>
|
||||
<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)]) name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ md5($value) }}" @readonly(true)>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e)
|
||||
@@ -13,7 +14,7 @@
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
{{ (($x=$o->hash($value)) && ($x::id() !== '*clear*')) ? sprintf('{%s}',$x::shortid()) : '' }}{{ str_repeat('*',16) }}
|
||||
{{ $o->render_item_old($langtag.'.'.$key) }}
|
||||
@endif
|
||||
@endforeach
|
||||
</x-attribute.layout>
|
||||
@@ -22,7 +23,7 @@
|
||||
<div class="row">
|
||||
<div class="offset-1 col-4 p-2">
|
||||
<span class="p-0 m-0">
|
||||
<button type="button" class="btn btn-sm btn-outline-dark mt-3" data-bs-toggle="modal" data-bs-target="#userpassword_check-modal"><i class="fas fa-user-check"></i> @lang('Check Password')</button>
|
||||
<button id="entry-userpassword-check" type="button" class="btn btn-sm btn-outline-dark mt-3" data-bs-toggle="modal" data-bs-target="#page-modal"><i class="fas fa-user-check"></i> @lang('Check Password')</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<!-- $o=RDN::class -->
|
||||
<x-attribute.layout :edit="$edit ?? FALSE" :new="$new ?? FALSE" :o="$o">
|
||||
@foreach($o->values as $value)
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o">
|
||||
@foreach(($o->values->count() ? $o->values : ['']) as $value)
|
||||
@if($edit)
|
||||
<div class="input-group has-validation mb-3">
|
||||
<select class="form-select @error('rdn')is-invalid @enderror" id="rdn" name="rdn">
|
||||
@@ -38,23 +38,23 @@
|
||||
rdn_attr = $('select#rdn').val();
|
||||
|
||||
if (rdn_attr) {
|
||||
$('#'+rdn_attr).find('input').attr('readonly',true);
|
||||
$('#'+rdn_attr).find('input').first().attr('readonly',true);
|
||||
set_rdn_value();
|
||||
}
|
||||
|
||||
function set_rdn_value() {
|
||||
if (rdn_attr && rdn_value_set)
|
||||
$('#'+rdn_attr).find('input').val($('input#rdn_value').val());
|
||||
$('#'+rdn_attr).find('input').first().val($('input#rdn_value').val());
|
||||
}
|
||||
|
||||
$('select#rdn').on('change',function() {
|
||||
// if rdn_attr is already set (and its now different), remove read only and clear value
|
||||
if (rdn_attr)
|
||||
$('#'+rdn_attr).find('input').attr('readonly',false).val('');
|
||||
$('#'+rdn_attr).find('input').first().attr('readonly',false).val('');
|
||||
|
||||
// set RDN attribute read-only
|
||||
if (rdn_attr = $(this).val())
|
||||
$('#'+rdn_attr).find('input').attr('readonly',true).val('');
|
||||
$('#'+rdn_attr).find('input').first().attr('readonly',true).val('');
|
||||
|
||||
set_rdn_value();
|
||||
})
|
||||
|
@@ -0,0 +1 @@
|
||||
{!! $o->values->join('<br>') !!}
|
@@ -0,0 +1,25 @@
|
||||
<!-- $o=UserCertificate::class -->
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o" langtag="binary">
|
||||
@foreach($o->tagValuesOld('binary') as $key => $value)
|
||||
@if($edit)
|
||||
<input type="hidden" name="name={{ $o->name_lc }}[binary][]" value="{{ md5($value) }}">
|
||||
|
||||
<div class="input-group has-validation mb-3">
|
||||
<textarea class="form-control mb-1 font-monospace" rows="{{ count(explode("\n",$x=$o->certificate())) }}" style="overflow: hidden; font-size: 90%;" disabled>{{ $x }}</textarea>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index))
|
||||
{{ join('|',$e) }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-helper">
|
||||
@lang('Certificate Subject'): <strong>{{ $o->subject($loop->index) }}</strong><br/>
|
||||
{{ ($expire=$o->expires($loop->index))->isPast() ? __('Expired') : __('Expires') }}: <strong>{{ $expire->format(config('pla.datetime_format','Y-m-d H:i:s')) }}</strong>
|
||||
</div>
|
||||
|
||||
@else
|
||||
<span class="form-control mb-1"><pre class="m-0">{{ $o->render_item_old('binary.'.$key) }}</pre></span>
|
||||
@endif
|
||||
@endforeach
|
||||
</x-attribute.layout>
|
@@ -1,9 +1,9 @@
|
||||
<span id="objectclass_{{$value}}">
|
||||
<div class="input-group has-validation">
|
||||
<!-- @todo Have an "x" to remove the entry, we need an event to process the removal, removing any attribute values along the way -->
|
||||
<input type="text" @class(['form-control','input-group-end','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$loop->index)),'mb-1','border-focus'=>$o->values->contains($value)]) name="{{ $o->name_lc }}[]" value="{{ $value }}" placeholder="{{ Arr::get($o->values,$loop->index,'['.__('NEW').']') }}" @readonly(true)>
|
||||
<input type="text" @class(['form-control','input-group-end','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)),'mb-1','border-focus'=>! $o->tagValuesOld($langtag)->contains($value)]) name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ $value }}" placeholder="{{ Arr::get($o->values,$loop->index,'['.__('NEW').']') }}" @readonly(true)>
|
||||
@if ($o->isStructural($value))
|
||||
<span class="input-group-end text-black-50">structural</span>
|
||||
<span class="input-group-end text-black-50">@lang('structural')</span>
|
||||
@else
|
||||
<span class="input-group-end"><i class="fas fa-fw fa-xmark"></i></span>
|
||||
@endif
|
||||
|
@@ -1,16 +1,14 @@
|
||||
@use(App\Classes\LDAP\Attribute\Binary\JpegPhoto)
|
||||
@use(App\Classes\LDAP\Attribute\ObjectClass)
|
||||
@use(App\Classes\LDAP\Attribute\UserCertificate)
|
||||
@php($clone=FALSE)
|
||||
@if($o->is_rdn)
|
||||
<span class="btn btn-sm btn-outline-focus mt-3"><i class="fas fa-fw fa-exchange"></i> @lang('Rename')</span>
|
||||
@elseif($edit && $o->can_addvalues)
|
||||
<span class="p-0 m-0">
|
||||
<span class="p-0 m-0">
|
||||
@if($o->is_rdn)
|
||||
<button class="btn btn-sm btn-outline-focus mt-3" disabled><i class="fas fa-fw fa-exchange"></i> @lang('Rename')</button>
|
||||
@elseif($edit && $o->can_addvalues)
|
||||
@switch(get_class($o))
|
||||
@case('App\Classes\LDAP\Attribute\Binary\JpegPhoto')
|
||||
<span @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name_lc }}"><i class="fas fa-fw fa-plus"></i> @lang('Upload JpegPhoto')</span>
|
||||
|
||||
@break
|
||||
|
||||
@case('App\Classes\LDAP\Attribute\ObjectClass')
|
||||
<button type="button" @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) data-bs-toggle="modal" data-bs-target="#new_objectclass-modal"><i class="fas fa-fw fa-plus"></i> @lang('Add Objectclass')</button>
|
||||
@case(ObjectClass::class)
|
||||
<span type="button" @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) data-bs-toggle="modal" data-bs-target="#new_objectclass-modal"><i class="fas fa-fw fa-plus"></i> @lang('Add Objectclass')</span>
|
||||
|
||||
<!-- NEW OBJECT CLASS -->
|
||||
<div class="modal fade" id="new_objectclass-modal" tabindex="-1" aria-labelledby="new_objectclass-label" aria-hidden="true" data-bs-backdrop="static">
|
||||
@@ -37,47 +35,20 @@
|
||||
$(document).ready(function() {
|
||||
var added_oc = []; // Object classes being added to this entry
|
||||
var rendered = false;
|
||||
var newadded = [];
|
||||
|
||||
// Show our ObjectClass modal so that we can add more objectclasses
|
||||
$('#new_objectclass-modal').on('shown.bs.modal',function() {
|
||||
if (! rendered)
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: '{{ url('entry/objectclass/add') }}',
|
||||
data: {
|
||||
oc: oc,
|
||||
},
|
||||
cache: false,
|
||||
success: function(data) {
|
||||
$('select#newoc').select2({
|
||||
dropdownParent: $('#new_objectclass-modal'),
|
||||
theme: 'bootstrap-5',
|
||||
multiple: true,
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
});
|
||||
|
||||
rendered = true;
|
||||
})
|
||||
|
||||
// When the ObjectClass modal is closed, process what was selected
|
||||
$('#new_objectclass-modal').on('hide.bs.modal',function() {
|
||||
var newadded = $('select#newoc').val();
|
||||
|
||||
// If nothing selected, we dont have anything to do
|
||||
if (added_oc.sort().join('|') === newadded.sort().join('|'))
|
||||
return;
|
||||
if (newadded.length)
|
||||
process_oc();
|
||||
|
||||
function process_oc() {
|
||||
// Find out what was selected, and add them
|
||||
newadded.forEach(function (item) {
|
||||
if (added_oc.indexOf(item) !== -1)
|
||||
return;
|
||||
|
||||
// Add our new OC to the list of OCs
|
||||
oc.push(item);
|
||||
|
||||
// Add attribute to the page
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
@@ -97,6 +68,7 @@
|
||||
},
|
||||
});
|
||||
|
||||
// Get a list of attributes already on the page, so we dont double up
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: '{{ url('api/schema/objectclass/attrs') }}/'+item,
|
||||
@@ -105,6 +77,9 @@
|
||||
// Render any must attributes
|
||||
if (data.must.length) {
|
||||
data.must.forEach(function(item) {
|
||||
if ($('attribute#'+item).length)
|
||||
return;
|
||||
|
||||
// Add attribute to the page
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
@@ -196,14 +171,83 @@
|
||||
});
|
||||
|
||||
added_oc = newadded;
|
||||
}
|
||||
|
||||
// Show our ObjectClass modal so that we can add more objectclasses
|
||||
$('#new_objectclass-modal').on('shown.bs.modal',function() {
|
||||
if (! rendered)
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: '{{ url('entry/objectclass/add') }}',
|
||||
data: {
|
||||
oc: oc,
|
||||
},
|
||||
cache: false,
|
||||
success: function(data) {
|
||||
$('select#newoc').select2({
|
||||
dropdownParent: $('#new_objectclass-modal'),
|
||||
theme: 'bootstrap-5',
|
||||
multiple: true,
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
});
|
||||
|
||||
rendered = true;
|
||||
})
|
||||
|
||||
// When the ObjectClass modal is closed, process what was selected
|
||||
$('#new_objectclass-modal').on('hide.bs.modal',function() {
|
||||
newadded = $('select#newoc').val();
|
||||
|
||||
// If nothing selected, we dont have anything to do
|
||||
if (added_oc.sort().join('|') === newadded.sort().join('|'))
|
||||
return;
|
||||
|
||||
process_oc();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@append
|
||||
@break
|
||||
|
||||
@case('App\Classes\LDAP\Attribute')
|
||||
@case(JpegPhoto::class)
|
||||
<span @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name }}-upload" disabled><i class="fas fa-fw fa-file-arrow-up"></i> @lang('Upload JpegPhoto')</span>
|
||||
@section('page-scripts')
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#{{ $o->name }}-upload.addable').click(function(e) {
|
||||
alert('Sorry, not implemented yet');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@append
|
||||
@break
|
||||
|
||||
@case(UserCertificate::class)
|
||||
<span @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name }}-replace" disabled><i class="fas fa-fw fa-certificate"></i> @lang('Replace Certificate')</span>
|
||||
@section('page-scripts')
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#{{ $o->name }}-replace.addable').click(function(e) {
|
||||
alert('Sorry, not implemented yet');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@append
|
||||
@break
|
||||
|
||||
<!-- All other attributes -->
|
||||
@default
|
||||
@if($o->isDynamic()) @break @endif
|
||||
@php($clone=TRUE)
|
||||
<span @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name }}-addnew"><i class="fas fa-fw fa-plus"></i> @lang('Add Value')</span>
|
||||
|
||||
@@ -214,13 +258,16 @@
|
||||
// Create a new entry when Add Value clicked
|
||||
$('#{{ $o->name }}-addnew.addable').click(function (item) {
|
||||
var cln = $(this).parent().parent().find('input:last').parent().clone();
|
||||
cln.find('input:last').attr('value','').attr('placeholder', '[@lang('NEW')]');
|
||||
cln.appendTo('#'+item.currentTarget.id.replace('-addnew',''));
|
||||
cln.find('input:last')
|
||||
.attr('value','')
|
||||
.attr('placeholder', '[@lang('NEW')]')
|
||||
.addClass('border-focus')
|
||||
.appendTo('#'+item.currentTarget.id.replace('-addnew',''));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
@append
|
||||
@endswitch
|
||||
</span>
|
||||
@endif
|
||||
@endif
|
||||
</span>
|
@@ -0,0 +1,8 @@
|
||||
<!-- $o=Attribute::class -->
|
||||
<x-attribute.layout :edit="false" :new="false" :o="$o" :detail="true">
|
||||
@foreach(Arr::get(old($o->name_lc,[$langtag=>$o->tagValues($langtag)]),$langtag,[]) as $value)
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control mb-1" value="{{ \Carbon\Carbon::createFromTimestamp(strtotime($value))->format(config('pla.datetime_format','Y-m-d H:i:s')) }}" disabled>
|
||||
</div>
|
||||
@endforeach
|
||||
</x-attribute.layout>
|
8
resources/views/components/form/disabled/input.blade.php
Normal file
8
resources/views/components/form/disabled/input.blade.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<!-- $o=Attribute::class -->
|
||||
<x-attribute.layout :edit="false" :new="false" :o="$o" :detail="true">
|
||||
@foreach(Arr::get(old($o->name_lc,[$langtag=>$o->tagValues($langtag)]),$langtag,[]) as $value)
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control mb-1" value="{{ $value }}" disabled>
|
||||
</div>
|
||||
@endforeach
|
||||
</x-attribute.layout>
|
@@ -66,7 +66,7 @@
|
||||
@endif
|
||||
|
||||
@isset($options)
|
||||
@if($options->count() === 1)
|
||||
@if(($autoselect ?? FALSE) && $options->count() === 1)
|
||||
$('#{{ $id ?? $name }}')
|
||||
.val('{{ $options->first()['id'] }}')
|
||||
.trigger("change")
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<p>{{ __('Entry updated') }}</p>
|
||||
<ul style="list-style-type: square;">
|
||||
@foreach (session()->pull('updated') as $key => $o)
|
||||
<li><abbr title="{{ $o->description }}">{{ $o->name }}</abbr>: {{ $o->values->map(fn($item,$key)=>$o->render_item_new($key))->join(',') }}</li>
|
||||
<li><abbr title="{{ $o->description }}">{{ $o->name }}</abbr>: {{ $o->values->dot()->filter()->join(',') }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -24,7 +24,7 @@
|
||||
<td>BaseDN(s)</td>
|
||||
<td>
|
||||
<table class="table table-sm table-borderless">
|
||||
@foreach(\App\Classes\LDAP\Server::baseDNs()->sort(function($item) { return $item->sortKey; }) as $item)
|
||||
@foreach($server->baseDNs()->sort(fn($item)=>$item->sort_key) as $item)
|
||||
<tr>
|
||||
<td class="ps-0">{{ $item->getDn() }}</td>
|
||||
</tr>
|
||||
@@ -36,7 +36,13 @@
|
||||
<!-- Schema DN -->
|
||||
<tr>
|
||||
<td>Schema DN</td>
|
||||
<td>{{ \App\Classes\LDAP\Server::schemaDN() }}</td>
|
||||
<td>{{ $server->schemaDN() }}</td>
|
||||
</tr>
|
||||
|
||||
<!-- Schema DN -->
|
||||
<tr>
|
||||
<td>Root URL</td>
|
||||
<td>{{ request()->root() }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@@ -13,10 +13,9 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 pt-2">
|
||||
<x-form.select id="newattr" label="Select from..." :options="$o->getMissingAttributes()->sortBy('name')->map(fn($item)=>['id'=>$item->name,'value'=>$item->name_lc])"/>
|
||||
<x-form.select id="newattr" label="Select from..." :options="$o->getMissingAttributes()->sortBy('name')->unique('name')->map(fn($item)=>['id'=>$item->name,'value'=>$item->name_lc])"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-2"></div>
|
||||
</div>
|
@@ -26,6 +26,12 @@
|
||||
<x-attribute :o="$o->getObject('entryuuid')" :na="__('Unknown')"/>
|
||||
</th>
|
||||
</tr>
|
||||
@if($langtags->count())
|
||||
<tr class="mt-1">
|
||||
<td class="p-0 pe-2">Tags</td>
|
||||
<th class="p-0">{{ $langtags->join(', ') }}</th>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
@@ -1,5 +1,4 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="col-12 col-xl-3">
|
||||
<select id="attributetype" class="form-control">
|
||||
<option value="-all-">-all-</option>
|
||||
@@ -106,6 +105,24 @@
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('Required by ObjectClasses')</td>
|
||||
<td>
|
||||
@if ($o->required_by_object_classes->count())
|
||||
@foreach ($o->required_by_object_classes as $class => $structural)
|
||||
@if($structural)
|
||||
<strong>
|
||||
@endif
|
||||
<a class="objectclass" id="{{ strtolower($class) }}" href="#{{ strtolower($class) }}">{{ $class }}</a>
|
||||
@if($structural)
|
||||
</strong>
|
||||
@endif
|
||||
@endforeach
|
||||
@else
|
||||
@lang('(none)')
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('Force as MAY by config')</td><td><strong>@lang($o->forced_as_may ? 'Yes' : 'No')</strong></td>
|
||||
</tr>
|
||||
|
@@ -1,12 +1,15 @@
|
||||
@use(App\Ldap\Entry)
|
||||
|
||||
@extends('layouts.dn')
|
||||
|
||||
@section('page_title')
|
||||
@include('fragment.dn.header',['o'=>($oo=config('server')->fetch(old('container',$container)))])
|
||||
@include('fragment.dn.header',[
|
||||
'o'=>($oo=$server->fetch(old('container',$container))),
|
||||
'langtags'=>collect(),
|
||||
])
|
||||
@endsection
|
||||
|
||||
@section('main-content')
|
||||
<x-error/>
|
||||
|
||||
<div class="row">
|
||||
<div class="offset-1 col-10">
|
||||
<div class="main-card mb-3 card">
|
||||
@@ -28,9 +31,10 @@
|
||||
<div class="col-12 col-md-6">
|
||||
<x-form.select
|
||||
id="objectclass"
|
||||
name="objectclass[]"
|
||||
name="objectclass[{{ Entry::TAG_NOTAG }}][]"
|
||||
old="objectclass.{{ Entry::TAG_NOTAG }}"
|
||||
:label="__('Select a Structural ObjectClass...')"
|
||||
:options="($oc=config('server')->schema('objectclasses'))
|
||||
:options="($oc=$server->schema('objectclasses'))
|
||||
->filter(fn($item)=>$item->isStructural())
|
||||
->sortBy(fn($item)=>$item->name_lc)
|
||||
->map(fn($item)=>['id'=>$item->name,'value'=>$item->name])"
|
||||
@@ -85,13 +89,13 @@
|
||||
});
|
||||
|
||||
// Our password type
|
||||
$('div#userPassword .form-select').each(function() {
|
||||
$('attribute#userPassword .form-select').each(function() {
|
||||
$(this).prop('disabled',false);
|
||||
})
|
||||
|
||||
$('.row.d-none').removeClass('d-none');
|
||||
$('.addable.d-none').removeClass('d-none');
|
||||
$('.deletable.d-none').removeClass('d-none');
|
||||
$('span.addable.d-none').removeClass('d-none');
|
||||
$('span.deletable.d-none').removeClass('d-none');
|
||||
$('#newattr-select.d-none').removeClass('d-none');
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,70 @@
|
||||
@use(App\Ldap\Entry)
|
||||
|
||||
@extends('layouts.dn')
|
||||
|
||||
@section('page_title')
|
||||
@include('fragment.dn.header',['o'=>($o=config('server')->fetch($dn))])
|
||||
@include('fragment.dn.header',[
|
||||
'o'=>($o ?? $o=$server->fetch($dn)),
|
||||
'langtags'=>($langtags=$o->getLangTags()
|
||||
->flatMap(fn($item)=>$item->values())
|
||||
->unique()
|
||||
->sort())
|
||||
])
|
||||
@endsection
|
||||
|
||||
@section('page_actions')
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="action-buttons float-end">
|
||||
<ul class="nav">
|
||||
@if(isset($page_actions) && $page_actions->get('create'))
|
||||
<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('New Child')" disabled><i class="fas fa-fw fa-diagram-project fs-5"></i></button>
|
||||
</li>
|
||||
@endif
|
||||
@if(isset($page_actions) && $page_actions->get('export'))
|
||||
<li>
|
||||
<span id="entry-export" 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('Export')"><i class="fas fa-fw fa-download fs-5"></i></button>
|
||||
</span>
|
||||
</li>
|
||||
@endif
|
||||
@if(isset($page_actions) && $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>
|
||||
</li>
|
||||
@endif
|
||||
@if(isset($page_actions) && $page_actions->get('edit'))
|
||||
<li>
|
||||
<button class="btn btn-outline-dark p-1 m-1" id="entry-edit" data-bs-toggle="tooltip" data-bs-placement="bottom" title="@lang('Edit Entry')"><i class="fas fa-fw fa-edit fs-5"></i></button>
|
||||
</li>
|
||||
@endif
|
||||
<!-- @todo Dont offer the delete button for an entry with children -->
|
||||
@if(isset($page_actions) && $page_actions->get('delete'))
|
||||
<li>
|
||||
<span id="entry-delete" data-bs-toggle="modal" data-bs-target="#page-modal">
|
||||
<button class="btn btn-outline-danger p-1 m-1" data-bs-custom-class="custom-tooltip-danger" data-bs-toggle="tooltip" data-bs-placement="bottom" title="@lang('Delete Entry')"><i class="fas fa-fw fa-trash-can fs-5"></i></button>
|
||||
</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
@if(($x=$o->getOtherTags()->filter(fn($item)=>$item->diff(['binary'])->count()))->count())
|
||||
<div class="ms-4 mt-4 alert alert-danger p-2" style="max-width: 30em; font-size: 0.80em;">
|
||||
This entry has [<strong>{!! $x->flatten()->join('</strong>, <strong>') !!}</strong>] tags used by [<strong>{!! $x->keys()->join('</strong>, <strong>') !!}</strong>] that cant be managed by PLA. You can though manage those tags with an LDIF import.
|
||||
</div>
|
||||
@elseif(($x=$o->getLangMultiTags())->count())
|
||||
<div class="ms-4 mt-4 alert alert-danger p-2" style="max-width: 30em; font-size: 0.80em;">
|
||||
This entry has multi-language tags used by [<strong>{!! $x->keys()->join('</strong>, <strong>') !!}</strong>] that cant be managed by PLA. You can though manage those lang tags with an LDIF import.
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('main-content')
|
||||
@@ -12,25 +75,69 @@
|
||||
<div class="main-card mb-3 card">
|
||||
<div class="card-body">
|
||||
<div class="card-header-tabs">
|
||||
<ul class="nav nav-tabs">
|
||||
<ul class="nav nav-tabs mb-0">
|
||||
<li class="nav-item"><a data-bs-toggle="tab" href="#attributes" class="nav-link active">@lang('Attributes')</a></li>
|
||||
<li class="nav-item"><a data-bs-toggle="tab" href="#internal" class="nav-link">@lang('Internal')</a></li>
|
||||
@env(['local'])
|
||||
<li class="nav-item"><a data-bs-toggle="tab" href="#debug" class="nav-link">@lang('Debug')</a></li>
|
||||
@endenv
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- All Attributes -->
|
||||
<div class="tab-pane active" id="attributes" role="tabpanel">
|
||||
<form id="dn-edit" method="POST" class="needs-validation" action="{{ url('entry/update/pending') }}" novalidate>
|
||||
<form id="dn-edit" method="POST" class="needs-validation" action="{{ url('entry/update/pending') }}" novalidate readonly>
|
||||
@csrf
|
||||
|
||||
<input type="hidden" name="dn" value="">
|
||||
<div class="card-header border-bottom-0">
|
||||
<div class="btn-actions-pane-right">
|
||||
<div role="group" class="btn-group-sm nav btn-group">
|
||||
@foreach($langtags->prepend(Entry::TAG_NOTAG)->push('+') as $tag)
|
||||
<a data-bs-toggle="tab" href="#tab-lang-{{ $tag ?: '_default' }}" class="btn btn-outline-light border-dark-subtle @if(! $loop->index) active @endif @if($loop->last)ndisabled @endif">
|
||||
@switch($tag)
|
||||
@case(Entry::TAG_NOTAG)
|
||||
<i class="fas fa-fw fa-border-none" data-bs-toggle="tooltip" data-bs-custom-class="custom-tooltip" title="@lang('No Lang Tag')"></i>
|
||||
@break
|
||||
|
||||
@foreach ($o->getVisibleAttributes() as $ao)
|
||||
<x-attribute-type :edit="true" :o="$ao"/>
|
||||
@endforeach
|
||||
@case('+')
|
||||
<!-- @todo To implement -->
|
||||
<i class="fas fa-fw fa-plus text-dark" data-bs-toggle="tooltip" data-bs-custom-class="custom-tooltip" title="@lang('Add Lang Tag')"></i>
|
||||
@break
|
||||
|
||||
@default
|
||||
<span class="f16" data-bs-toggle="tooltip" data-bs-custom-class="custom-tooltip" title="{{ strtoupper($tag) }}"><i class="flag {{ $tag }}"></i></span>
|
||||
@endswitch
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
@foreach($langtags as $tag)
|
||||
<div class="tab-pane @if(! $loop->index) active @endif" id="tab-lang-{{ $tag ?: '_default' }}" role="tabpanel">
|
||||
@switch($tag)
|
||||
@case(Entry::TAG_NOTAG)
|
||||
@foreach ($o->getVisibleAttributes($tag) as $ao)
|
||||
<x-attribute-type :edit="true" :o="$ao" :langtag="$tag"/>
|
||||
@endforeach
|
||||
@break
|
||||
|
||||
@case('+')
|
||||
<div class="ms-auto mt-4 alert alert-warning p-2" style="max-width: 30em; font-size: 0.80em;">
|
||||
It is not possible to create new language tags at the moment. This functionality should come soon.<br>
|
||||
You can create them with an LDIF import though.
|
||||
</div>
|
||||
@break
|
||||
|
||||
@default
|
||||
@foreach ($o->getVisibleAttributes($langtag=sprintf('lang-%s',$tag)) as $ao)
|
||||
<x-attribute-type :edit="true" :o="$ao" :langtag="$langtag"/>
|
||||
@endforeach
|
||||
@endswitch
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('fragment.dn.add_attr')
|
||||
</form>
|
||||
@@ -44,26 +151,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Internal Attributes -->
|
||||
<div class="tab-pane" id="internal" role="tabpanel">
|
||||
<div class="tab-pane mt-3" id="internal" role="tabpanel">
|
||||
@foreach ($o->getInternalAttributes() as $ao)
|
||||
<x-attribute-type :o="$ao"/>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- Debug -->
|
||||
<div class="tab-pane" id="debug" role="tabpanel">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
@dump($o)
|
||||
</div>
|
||||
<div class="col-4">
|
||||
@dump($o->getAttributes())
|
||||
</div>
|
||||
<div class="col-4">
|
||||
@dump(['available'=>$o->getAvailableAttributes()->pluck('name'),'missing'=>$o->getMissingAttributes()->pluck('name')])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -77,63 +169,6 @@
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- EXPORT -->
|
||||
<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-lg-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-sm btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" id="entry_export-download">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($up=$o->getObject('userpassword'))
|
||||
<!-- CHECK USERPASSWORD -->
|
||||
<div class="modal fade" id="userpassword_check-modal" tabindex="-1" aria-labelledby="userpassword_check-label" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-fullscreen-lg-down">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="userpassword_check-label">Check Passwords for {{ $dn }}</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<table class="table table-bordered p-1">
|
||||
@foreach($up->values as $key => $value)
|
||||
<tr>
|
||||
<th>Check</th>
|
||||
<td>{{ $up->render_item_old($key) }}</td>
|
||||
<td>
|
||||
<input type="password" style="width: 90%" name="password[{{$key}}]"> <i class="fas fa-fw fa-lock"></i>
|
||||
<div class="invalid-feedback pb-2">
|
||||
Invalid Password
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" id="userpassword_check-submit"><i class="fas fa-fw fa-spinner fa-spin d-none"></i> Check</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
@section('page-scripts')
|
||||
@@ -141,24 +176,15 @@
|
||||
var dn = '{{ $o->getDNSecure() }}';
|
||||
var oc = {!! $o->getObject('objectclass')->values !!};
|
||||
|
||||
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() {
|
||||
$('#dn-edit input[name="dn"]').val(dn);
|
||||
|
||||
$('form#dn-edit').attr('readonly',false);
|
||||
$('button[id=entry-edit]')
|
||||
.removeClass('btn-outline-dark')
|
||||
.addClass('btn-dark');
|
||||
.addClass('btn-dark')
|
||||
.addClass('opacity-100')
|
||||
.attr('disabled',true);
|
||||
|
||||
// Find all input items and turn off readonly
|
||||
$('input.form-control').each(function() {
|
||||
@@ -170,13 +196,13 @@
|
||||
});
|
||||
|
||||
// Our password type
|
||||
$('div#userPassword .form-select').each(function() {
|
||||
$('attribute#userPassword .form-select').each(function() {
|
||||
$(this).prop('disabled',false);
|
||||
})
|
||||
|
||||
$('.row.d-none').removeClass('d-none');
|
||||
$('.addable.d-none').removeClass('d-none');
|
||||
$('.deletable.d-none').removeClass('d-none');
|
||||
$('span.addable.d-none').removeClass('d-none');
|
||||
$('span.deletable.d-none').removeClass('d-none');
|
||||
|
||||
@if($o->getMissingAttributes()->count())
|
||||
$('#newattr-select.d-none').removeClass('d-none');
|
||||
@@ -184,6 +210,15 @@
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('button[id=entry-edit]').on('click',function(item) {
|
||||
item.preventDefault();
|
||||
|
||||
if ($(this).hasClass('btn-dark'))
|
||||
return;
|
||||
|
||||
editmode();
|
||||
});
|
||||
|
||||
$('#newattr').on('change',function(item) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
@@ -192,7 +227,7 @@
|
||||
$('#newattrs').append(data);
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status != 412)
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
url: '{{ url('entry/attr/add') }}/'+item.target.value,
|
||||
@@ -210,13 +245,6 @@
|
||||
$('#newattr-select').remove();
|
||||
});
|
||||
|
||||
$('#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());
|
||||
});
|
||||
|
||||
$('#page-modal').on('shown.bs.modal',function(item) {
|
||||
var that = $(this).find('.modal-content');
|
||||
|
||||
@@ -234,7 +262,63 @@
|
||||
that.empty().html(data);
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status != 412)
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
})
|
||||
break;
|
||||
|
||||
case 'entry-export':
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
url: '{{ url('modal/export') }}/'+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);
|
||||
|
||||
that = $('#entry_export');
|
||||
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
url: '{{ url('entry/export') }}/'+dn,
|
||||
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().append(data);
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
})
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
})
|
||||
break;
|
||||
|
||||
case 'entry-userpassword-check':
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
url: '{{ url('modal/userpassword-check') }}/'+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....');
|
||||
},
|
||||
})
|
||||
@@ -245,78 +329,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
$('#entry_export-modal').on('shown.bs.modal',function() {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
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') }}/'+dn,
|
||||
cache: false
|
||||
})
|
||||
})
|
||||
|
||||
@if($up)
|
||||
$('button[id=userpassword_check-submit]').on('click',function(item) {
|
||||
var that = $(this);
|
||||
|
||||
var passwords = $('#userpassword_check-modal')
|
||||
.find('input[name^="password["')
|
||||
.map((key,item)=>item.value);
|
||||
|
||||
if (passwords.length === 0) return false;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
beforeSend: function() {
|
||||
// Disable submit, add spinning icon
|
||||
that.prop('disabled',true);
|
||||
that.find('i').removeClass('d-none');
|
||||
},
|
||||
complete: function() {
|
||||
that.prop('disabled',false);
|
||||
that.find('i').addClass('d-none');
|
||||
},
|
||||
success: function(data) {
|
||||
data.forEach(function(item,key) {
|
||||
var i = $('#userpassword_check-modal')
|
||||
.find('input[name="password['+key+']')
|
||||
.siblings('i');
|
||||
|
||||
var feedback = $('#userpassword_check-modal')
|
||||
.find('input[name="password['+key+']')
|
||||
.siblings('div.invalid-feedback');
|
||||
|
||||
if (item === 'OK') {
|
||||
i.removeClass('text-danger').addClass('text-success').removeClass('fa-lock').addClass('fa-lock-open');
|
||||
if (feedback.is(':visible'))
|
||||
feedback.hide();
|
||||
} else {
|
||||
i.removeClass('text-success').addClass('text-danger').removeClass('fa-lock-open').addClass('fa-lock');
|
||||
if (! feedback.is(':visible'))
|
||||
feedback.show();
|
||||
}
|
||||
})
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status != 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
url: '{{ url('entry/password/check') }}',
|
||||
data: {
|
||||
dn: dn,
|
||||
password: Array.from(passwords),
|
||||
},
|
||||
dataType: 'json',
|
||||
cache: false
|
||||
})
|
||||
});
|
||||
@endif
|
||||
|
||||
@if(old())
|
||||
editmode();
|
||||
@endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<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>@lang('To Server') <strong>{{ config('server')->name }}</strong></small></td>
|
||||
<td class="top text-start align-text-top p-2"><strong>@lang('LDIF Import')</strong><br><small>@lang('To Server') <strong>{{ $server->name }}</strong></small></td>
|
||||
</tr>
|
||||
</table>
|
||||
@endsection
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<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>@lang('To Server') <strong>{{ config('server')->name }}</strong></small></td>
|
||||
<td class="top text-start align-text-top p-0 pt-2"><strong>@lang('LDIF Import Result')</strong><br><small>@lang('To Server') <strong>{{ $server->name }}</strong></small></td>
|
||||
</tr>
|
||||
</table>
|
||||
@endsection
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<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-2"><strong>@lang('Server Info')</strong><br><small>{{ $server->rootDSE()->entryuuid[0] ?? '' }}</small></td>
|
||||
</tr>
|
||||
</table>
|
||||
@endsection
|
||||
@@ -13,10 +13,10 @@
|
||||
<div class="main-card mb-3 card">
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
@foreach ($s->rootDSE()->getObjects() as $attribute => $ao)
|
||||
@foreach ($server->rootDSE()->getObjects() as $attribute => $ao)
|
||||
<tr>
|
||||
<th class="w-25">
|
||||
{!! ($x=$s->schema('attributetypes',$attribute))
|
||||
{!! ($x=$server->schema('attributetypes',$attribute))
|
||||
? sprintf('<a class="attributetype" id="strtolower(%s)" href="%s">%s</a>',$x->name_lc,url('schema/attributetypes',$x->name_lc),$x->name)
|
||||
: $attribute !!}
|
||||
</th>
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td style="border-radius: 5px;"><div class="page-title-icon f32"><i class="fas fa-fingerprint"></i></div></td>
|
||||
<td class="top text-end align-text-top p-0 pt-2"><strong>{{ Server::schemaDN() }}</strong></td>
|
||||
<td class="top text-end align-text-top p-2"><strong>{{ Server::schemaDN() }}</strong></td>
|
||||
</tr>
|
||||
</table>
|
||||
@endsection
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<div class="modal-header bg-danger text-white">
|
||||
<h1 class="modal-title fs-5" id="entry_export-label">
|
||||
<h1 class="modal-title fs-5">
|
||||
<i class="fas fa-fw fa-exclamation-triangle"></i> <strong>@lang('WARNING')</strong>: @lang('Delete') <strong>{{ Crypt::decryptString($dn) }}</strong>
|
||||
</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
|
37
resources/views/modals/entry-export.blade.php
Normal file
37
resources/views/modals/entry-export.blade.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<div class="modal-header bg-dark text-white">
|
||||
<h1 class="modal-title fs-5">
|
||||
LDIF for {{ Crypt::decryptString($dn) }}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div id="entry_export"></div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<x-modal.close/>
|
||||
<button id="entry_export-download" type="button" class="btn btn-sm btn-primary">@lang('Download')</button>
|
||||
</div>
|
||||
|
||||
<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);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('button[id=entry_export-download]').on('click',function(item) {
|
||||
item.preventDefault();
|
||||
|
||||
let ldif = $('#entry_export').find('pre:first');
|
||||
download('ldap-export.ldif',ldif.html());
|
||||
});
|
||||
});
|
||||
</script>
|
86
resources/views/modals/entry-userpassword-check.blade.php
Normal file
86
resources/views/modals/entry-userpassword-check.blade.php
Normal file
@@ -0,0 +1,86 @@
|
||||
@php
|
||||
$o = $server->fetch(Crypt::decryptString($dn))
|
||||
@endphp
|
||||
<div class="modal-header bg-dark text-white">
|
||||
<h1 class="modal-title fs-5">Check Passwords for {{ $o->getDN() }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<table class="table table-bordered p-1">
|
||||
@foreach(($up=$o->getObject('userpassword'))->values->dot() as $dotkey => $value)
|
||||
<tr>
|
||||
<th>Check</th>
|
||||
<td>{{ $up->render_item_old($dotkey) }}</td>
|
||||
<td>
|
||||
<input type="password" style="width: 90%" name="password[{{ $dotkey }}]"> <i class="fas fa-fw fa-lock"></i>
|
||||
<div class="invalid-feedback pb-2">
|
||||
@lang('Invalid Password')
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<x-modal.close/>
|
||||
<button id="userpassword_check-submit" type="button" class="btn btn-sm btn-primary"><i class="fas fa-fw fa-spinner fa-spin d-none"></i> @lang('Check')</button>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('button[id=userpassword_check-submit]').on('click',function(item) {
|
||||
var that = $(this);
|
||||
|
||||
var passwords = $('#page-modal')
|
||||
.find('input[name^="password["')
|
||||
.map((key,item)=>item.value);
|
||||
|
||||
if (passwords.length === 0) return false;
|
||||
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: '{{ url('entry/password/check') }}',
|
||||
data: {
|
||||
dn: dn,
|
||||
password: Array.from(passwords),
|
||||
},
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: function() {
|
||||
// Disable submit, add spinning icon
|
||||
that.prop('disabled',true);
|
||||
that.find('i').removeClass('d-none');
|
||||
},
|
||||
complete: function() {
|
||||
that.prop('disabled',false);
|
||||
that.find('i').addClass('d-none');
|
||||
},
|
||||
success: function(data) {
|
||||
data.forEach(function(item,key) {
|
||||
var i = $('#page-modal')
|
||||
.find('input[name="password['+key+']')
|
||||
.siblings('i');
|
||||
|
||||
var feedback = $('#page-modal')
|
||||
.find('input[name="password['+key+']')
|
||||
.siblings('div.invalid-feedback');
|
||||
|
||||
if (item === 'OK') {
|
||||
i.removeClass('text-danger').addClass('text-success').removeClass('fa-lock').addClass('fa-lock-open');
|
||||
if (feedback.is(':visible'))
|
||||
feedback.hide();
|
||||
|
||||
} else {
|
||||
i.removeClass('text-success').addClass('text-danger').removeClass('fa-lock-open').addClass('fa-lock');
|
||||
if (! feedback.is(':visible'))
|
||||
feedback.show();
|
||||
}
|
||||
})
|
||||
},
|
||||
error: function(e) {
|
||||
if (e.status !== 412)
|
||||
alert('That didnt work? Please try again....');
|
||||
},
|
||||
})
|
||||
});
|
||||
</script>
|
@@ -1,7 +1,12 @@
|
||||
@extends('home')
|
||||
|
||||
@section('page_title')
|
||||
@include('fragment.dn.header')
|
||||
@include('fragment.dn.header',[
|
||||
'langtags'=>($langtags=$o->getLangTags()
|
||||
->flatMap(fn($item)=>$item->values())
|
||||
->unique()
|
||||
->sort())
|
||||
])
|
||||
@endsection
|
||||
|
||||
@section('main-content')
|
||||
@@ -23,25 +28,35 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Attribute</th>
|
||||
<th>Tag</th>
|
||||
<th>OLD</th>
|
||||
<th>NEW</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach ($o->getAttributesAsObjects()->filter(fn($item)=>$item->isDirty()) as $key => $oo)
|
||||
@foreach ($o->getObjects()->filter(fn($item)=>$item->isDirty()) as $key => $oo)
|
||||
<tr>
|
||||
<th rowspan="{{ $x=max($oo->values->keys()->max(),$oo->old_values->keys()->max())+1}}">
|
||||
<th rowspan="{{ $x=max($oo->values->dot()->keys()->count(),$oo->values_old->dot()->keys()->count())+1}}">
|
||||
<abbr title="{{ $oo->description }}">{{ $oo->name }}</abbr>
|
||||
</th>
|
||||
@for($xx=0;$xx<$x;$xx++)
|
||||
@if($xx)
|
||||
|
||||
@foreach($oo->values->dot()->keys()->merge($oo->values_old->dot()->keys())->unique() as $dotkey)
|
||||
@if($loop->index)
|
||||
</tr><tr>
|
||||
@endif
|
||||
|
||||
<td>{{ $oo->render_item_old($xx) ?: '['.strtoupper(__('New Value')).']' }}</td>
|
||||
<td>{{ $oo->render_item_new($xx) ?: '['.strtoupper(__('Deleted')).']' }}<input type="hidden" name="{{ $key }}[]" value="{{ Arr::get($oo->values,$xx) }}"></td>
|
||||
@endfor
|
||||
<th>
|
||||
{{ $dotkey }}
|
||||
</th>
|
||||
|
||||
@if((! Arr::get($oo->values_old->dot(),$dotkey)) && (! Arr::get($oo->values->dot(),$dotkey)))
|
||||
<td colspan="2" class="text-center">@lang('Ignoring blank value')</td>
|
||||
@else
|
||||
<td>{{ (($r=$oo->render_item_old($dotkey)) !== NULL) ? $r : '['.strtoupper(__('New Value')).']' }}</td>
|
||||
<td>{{ (($r=$oo->render_item_new($dotkey)) !== NULL) ? $r : '['.strtoupper(__('Deleted')).']' }}<input type="hidden" name="{{ $key }}[{{ collect(explode('.',$dotkey))->first() }}][]" value="{{ Arr::get($oo,$dotkey) }}"></td>
|
||||
@endif
|
||||
@endforeach
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
|
@@ -31,10 +31,10 @@ Route::get('logout',[LoginController::class,'logout']);
|
||||
Route::controller(HomeController::class)->group(function() {
|
||||
Route::middleware(AllowAnonymous::class)->group(function() {
|
||||
Route::get('/','home');
|
||||
Route::get('info','info');
|
||||
Route::get('debug','debug');
|
||||
Route::view('info','frames.info');
|
||||
Route::view('debug','debug');
|
||||
Route::post('frame','frame');
|
||||
Route::get('import','import_frame');
|
||||
Route::view('import','frames.import');
|
||||
Route::get('schema','schema_frame');
|
||||
|
||||
Route::group(['prefix'=>'user'],function() {
|
||||
@@ -54,5 +54,7 @@ Route::controller(HomeController::class)->group(function() {
|
||||
Route::post('import/process/{type}','import');
|
||||
|
||||
Route::view('modal/delete/{dn}','modals.entry-delete');
|
||||
Route::view('modal/export/{dn}','modals.entry-export');
|
||||
Route::view('modal/userpassword-check/{dn}','modals.entry-userpassword-check');
|
||||
});
|
||||
});
|
@@ -1,7 +1,6 @@
|
||||
|
||||
dn: cn=tech_staff,dc=example.com
|
||||
cn: tech_staff
|
||||
labeleduri: ldap:///ou=People,o=Simpsons?uid?one?(&(sn=Simpson)(|(uidNumber
|
||||
=1000)(uidNumber=1001)))
|
||||
labeleduri: ldap:///dc=example.com?uid?one?(|(cn=kerberos)(uidNumber=*))
|
||||
objectclass: nisMailAlias
|
||||
objectclass: labeledURIObject
|
||||
|
24
tests/server/openldap/data/04-z_usercert.ldif
Normal file
24
tests/server/openldap/data/04-z_usercert.ldif
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
dn: uid=usercert,dc=example.com
|
||||
cn: cn
|
||||
gidNumber: 1111
|
||||
homeDirectory: /home
|
||||
objectClass: account
|
||||
objectClass: posixAccount
|
||||
objectClass: pkiUser
|
||||
uid: usercert
|
||||
uidNumber: 111
|
||||
usercertificate;binary:: MIIC2TCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQQFADBGMQswCQYD
|
||||
VQQGEwJJVDENMAsGA1UEChMESU5GTjESMBAGA1UECxMJQXV0aG9yaXR5MRQwEgYDVQQDEwtJTkZO
|
||||
IENBICgyKTAeFw05OTA2MjMxMTE2MDdaFw0wMzA4MDExMTE2MDdaMEYxCzAJBgNVBAYTAklUMQ0w
|
||||
CwYDVQQKEwRJTkZOMRIwEAYDVQQLEwlBdXRob3JpdHkxFDASBgNVBAMTC0lORk4gQ0EgKDIpMIGf
|
||||
MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrHdRKJsobcjXz/OsGjyq8v73DbggG3JCGrQZ9f1Vm
|
||||
9RrIWJPwggczqgxwWL6JLPKglxbUjAtUxiZm3fw2kX7FGMUq5JaN/Pk2PT4ExA7bYLnbLGZ9jKJs
|
||||
Dh4bNOKrGRIxRO9Ff+YwmH8EQdoVpSRFbBpNnoDIkHLc4DtzB+B4wwIDAQABo4HWMIHTMAwGA1Ud
|
||||
EwQFMAMBAf8wHQYDVR0OBBYEFK3QjOXGc4j9LqYEYTn9WvSRAcusMG4GA1UdIwRnMGWAFK3QjOXG
|
||||
c4j9LqYEYTn9WvSRAcusoUqkSDBGMQswCQYDVQQGEwJJVDENMAsGA1UEChMESU5GTjESMBAGA1UE
|
||||
CxMJQXV0aG9yaXR5MRQwEgYDVQQDEwtJTkZOIENBICgyKYIBADALBgNVHQ8EBAMCAQYwEQYJYIZI
|
||||
AYb4QgEBBAQDAgAHMAkGA1UdEQQCMAAwCQYDVR0SBAIwADANBgkqhkiG9w0BAQQFAAOBgQCDs5b1
|
||||
jmbIYVq2epd5iDjQ109SJ/V7b6DFw2NIl8CWeDPOOjL1E5M8dnlmCDeTR2TlBxqUZaBBJZPqzFdv
|
||||
xpxqsHC0HfkCXAnUe5MaefFNAH9WbxoB/A2pkXtT6WGWed+QsL5wyKJaO4oD9UD5T+x12aGsHcsD
|
||||
Cy3EVEaGEOl+/A==
|
7
tests/server/openldap/schema/modify/00-config.ldif
Normal file
7
tests/server/openldap/schema/modify/00-config.ldif
Normal file
@@ -0,0 +1,7 @@
|
||||
dn: olcDatabase={0}config,cn=config
|
||||
changetype: modify
|
||||
add: olcRootPW
|
||||
olcRootPW:: c2VjcmV0
|
||||
|
||||
add: olcRootDN
|
||||
olcRootDN: cn=config
|
25
tests/server/openldap/schema/modify/00-mapsize.ldif
Normal file
25
tests/server/openldap/schema/modify/00-mapsize.ldif
Normal file
@@ -0,0 +1,25 @@
|
||||
dn: olcDatabase={3}mdb,cn=config
|
||||
changetype: modify
|
||||
add: olcDbMaxSize
|
||||
olcDbMaxSize: 1073741824
|
||||
|
||||
dn: olcDatabase={4}mdb,cn=config
|
||||
changetype: modify
|
||||
add: olcDbMaxSize
|
||||
olcDbMaxSize: 1073741824
|
||||
|
||||
dn: olcDatabase={5}mdb,cn=config
|
||||
changetype: modify
|
||||
add: olcDbMaxSize
|
||||
olcDbMaxSize: 1073741824
|
||||
|
||||
dn: olcDatabase={6}mdb,cn=config
|
||||
changetype: modify
|
||||
add: olcDbMaxSize
|
||||
olcDbMaxSize: 1073741824
|
||||
|
||||
dn: olcDatabase={7}mdb,cn=config
|
||||
changetype: modify
|
||||
add: olcDbMaxSize
|
||||
olcDbMaxSize: 1073741824
|
||||
|
4
tests/server/openldap/schema/modify/00-sizelimit.ldif
Normal file
4
tests/server/openldap/schema/modify/00-sizelimit.ldif
Normal file
@@ -0,0 +1,4 @@
|
||||
dn: olcDatabase={4}mdb,cn=config
|
||||
changetype: modify
|
||||
add: olcSizeLimit
|
||||
olcSizeLimit: 2000
|
11
tests/server/openldap/schema/modify/40-dynlist-options.ldif
Normal file
11
tests/server/openldap/schema/modify/40-dynlist-options.ldif
Normal file
@@ -0,0 +1,11 @@
|
||||
dn: olcOverlay=dynlist,olcDatabase={4}mdb,cn=config
|
||||
changetype: add
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcDynListConfig
|
||||
olcOverlay: dynlist
|
||||
olcDynListAttrSet: nisMailAlias labeledURI
|
||||
#olcDynListAttrSet: groupOfURLs memberURL memberOf
|
||||
#olcDynListAttrSet: groupOfURLs memberURL member+dgMemberOf
|
||||
#olcDynListAttrSet: groupOfURLs memberURL member
|
||||
#olcDynListAttrSet: groupOfURLs memberURL member
|
||||
#olcDynListAttrSet: groupOfURLs labeledURI member
|
Reference in New Issue
Block a user