Compare commits
51 Commits
b08de519d4
...
9798863e34
Author | SHA1 | Date | |
---|---|---|---|
9798863e34 | |||
4494154879 | |||
b22c9505bc | |||
29a659ff69 | |||
2348da36c4 | |||
6f58f5db36 | |||
553368c7b9 | |||
c8d1122ff6 | |||
2320445dfb | |||
6d2c9d1354 | |||
6f20d426ad | |||
7b1b4f4e50 | |||
543250e1fb | |||
3bf97fc0d1 | |||
3ad4c446ea | |||
ee3cb395c2 | |||
29c39e618f | |||
647cee9858 | |||
54c0df2597 | |||
67d65b3a98 | |||
9547b5fc5a | |||
f6b7bff605 | |||
e8aaa17122 | |||
ee7762d69b | |||
fac560750e | |||
d3aa73e468 | |||
2ddeff8ed3 | |||
b6bce380dd | |||
8fd2a43ee2 | |||
96afbd8316 | |||
5ce3a63878 | |||
ac8e79ab99 | |||
d0c02b91c0 | |||
2a691c147e | |||
781c87cb83 | |||
98a0b87afe | |||
88db4ccc99 | |||
6059bc1e45 | |||
acf19cdc5b | |||
56fcd729e7 | |||
d61f6168a4 | |||
f2eaed247a | |||
31e3c75bc9 | |||
9f0290bd40 | |||
820f398c2c | |||
8602c2b17f | |||
33d96940e6 | |||
06b7c204b0 | |||
7854cbdabd | |||
32514c9ab1 | |||
db600a28d3 |
@ -2,7 +2,6 @@ APP_NAME=Laravel
|
||||
APP_ENV=production
|
||||
APP_KEY=
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://localhost
|
||||
|
||||
LOG_CHANNEL=daily
|
||||
|
||||
@ -12,7 +11,7 @@ SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
LDAP_HOST=
|
||||
LDAP_BASE_DN=
|
||||
LDAP_USERNAME=
|
||||
LDAP_PASSWORD=
|
||||
LDAP_CACHE=false
|
||||
LDAP_ALERT_ROOTDN=true
|
||||
|
@ -2,7 +2,6 @@ APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
LOG_CHANNEL=stderr
|
||||
|
||||
@ -12,7 +11,6 @@ SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
LDAP_HOST=openldap
|
||||
LDAP_BASE_DN="dc=Test"
|
||||
LDAP_USERNAME="cn=admin,dc=Test"
|
||||
LDAP_PASSWORD="test"
|
||||
LDAP_CACHE=false
|
||||
|
@ -3,7 +3,7 @@ run-name: ${{ gitea.actor }} Building Docker Image 🐳
|
||||
on: [push]
|
||||
env:
|
||||
DOCKER_HOST: tcp://127.0.0.1:2375
|
||||
ASSETS: 3fcb8707
|
||||
ASSETS: 2d732e5
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
51
README.md
51
README.md
@ -1,4 +1,10 @@
|
||||
# phpLDAPadmin
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
phpLDAPadmin is a web based LDAP data management tool for system administrators. It is commonly known and referred by many as "PLA".
|
||||
|
||||
PLA is designed to be compliant with LDAP RFCs, enabling it to be used with any LDAP server.
|
||||
@ -27,38 +33,29 @@ Take a look at the [Docker Container](https://github.com/leenooks/phpLDAPadmin/w
|
||||
>
|
||||
> Open an issue (details below) with enough information for me to be able to recreate the problem. An `LDIF` will be invaluable if it is not handling data correctly.
|
||||
|
||||
## Version 2 Progress
|
||||
## Templates
|
||||
Starting with v2.2, PLA reintroduces the template engine. Each point release going forward will improve the template
|
||||
functionality. Check [releases](https://github.com/leenooks/phpLDAPadmin/releases) for details.
|
||||
|
||||
The update to v2 is progressing well - here is a list of work to do and done:
|
||||
Templates in v2 are in JSON format (in v1 they were XML format). If you want to create your own templates you can use
|
||||
the [example.json](/templates/example.json) template as a guide. Place your custom templates in a subdirectory
|
||||
under `templates`, eg: `templates/custom`, and they wont be overwritten by an update.
|
||||
|
||||
- [X] Creating new LDAP entries
|
||||
- [X] Delete existing LDAP entries
|
||||
- [X] Updating existing LDAP Entries
|
||||
- [X] Password attributes
|
||||
- [X] Support different password hash options
|
||||
- [X] Validate password is correct
|
||||
## Outstanding items
|
||||
Compare to v1.x, there are a couple of outstanding items to address
|
||||
|
||||
Entry Editing:
|
||||
- [ ] JpegPhoto Create/Delete
|
||||
- [ ] Binary attribute upload
|
||||
- [X] JpegPhoto Display
|
||||
- [X] ObjectClass Add/Remove
|
||||
- [X] Add additional required attributes (for ObjectClass Addition)
|
||||
- [ ] Remove existing required attributes (for ObjectClass Removal)
|
||||
- [X] Add additional values to Attributes that support multiple values
|
||||
- [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
|
||||
- [X] Export entries as an LDAP
|
||||
- [X] Import LDIF
|
||||
- [X] Schema Browser
|
||||
- [X] Searching
|
||||
- [ ] Enforcing attribute uniqueness
|
||||
- [ ] Is there something missing?
|
||||
- [ ] If removing an objectClass, remove all attributes that only that objectclass provided
|
||||
|
||||
Support is known for these LDAP servers:
|
||||
Templates Engine
|
||||
- [ ] Present SELECT lists when an attribute is marked as `type=select`
|
||||
- [ ] Enforcing attribute uniqueness
|
||||
|
||||
Raise a [feature request](https://github.com/leenooks/phpLDAPadmin/issues/new) if there is a capability that you would like to see added to PLA.
|
||||
|
||||
## Support is known for these LDAP servers:
|
||||
- [X] OpenLDAP
|
||||
- [X] OpenDJ
|
||||
- [ ] Microsoft Active Directory
|
||||
|
@ -7,6 +7,8 @@ use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Classes\LDAP\Schema\AttributeType;
|
||||
use App\Classes\Template;
|
||||
use App\Exceptions\InvalidUsage;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
/**
|
||||
@ -14,9 +16,6 @@ use App\Ldap\Entry;
|
||||
*/
|
||||
class Attribute implements \Countable, \ArrayAccess
|
||||
{
|
||||
// Attribute Name
|
||||
protected string $name;
|
||||
|
||||
// Is this attribute an internal attribute
|
||||
protected ?bool $_is_internal = NULL;
|
||||
protected(set) bool $no_attr_tags = FALSE;
|
||||
@ -98,11 +97,11 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
* @param string $name Name of the attribute
|
||||
* @param array $values Current Values
|
||||
* @param array $oc The objectclasses that the DN of this attribute has
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function __construct(string $dn,string $name,array $values,array $oc=[])
|
||||
{
|
||||
$this->dn = $dn;
|
||||
$this->name = $name;
|
||||
$this->_values = collect($values);
|
||||
$this->_values_old = collect($values);
|
||||
|
||||
@ -113,8 +112,12 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
|
||||
// Get the objectclass heirarchy for required attribute determination
|
||||
foreach ($oc as $objectclass) {
|
||||
$this->oc->push($objectclass);
|
||||
$this->oc = $this->oc->merge(config('server')->schema('objectclasses',$objectclass)->getParents()->pluck('name'));
|
||||
$soc = config('server')->schema('objectclasses',$objectclass);
|
||||
|
||||
if ($soc) {
|
||||
$this->oc->push($soc->oid);
|
||||
$this->oc = $this->oc->merge($soc->getParents()->pluck('oid'));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -140,8 +143,6 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
return match ($key) {
|
||||
// List all the attributes
|
||||
'attributes' => $this->attributes(),
|
||||
// Can this attribute have more values
|
||||
'can_addvalues' => $this->schema && (! $this->schema->is_single_value) && ((! $this->max_values_count) || ($this->values->count() < $this->max_values_count)),
|
||||
// Schema attribute description
|
||||
@ -164,7 +165,7 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
// 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},
|
||||
'name' => $this->schema->{$key},
|
||||
// Attribute name in lower case
|
||||
'name_lc' => strtolower($this->name),
|
||||
// Required by Object Classes
|
||||
@ -268,9 +269,6 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
if ($this->is_rdn)
|
||||
$result->put(__('rdn'),__('This attribute is required for the RDN'));
|
||||
|
||||
// If this attribute name is an alias for the schema attribute name
|
||||
// @todo
|
||||
|
||||
if ($this->required()->count())
|
||||
$result->put(__('required'),sprintf('%s: %s',__('Required Attribute by ObjectClass(es)'),$this->required()->join(', ')));
|
||||
|
||||
@ -302,7 +300,7 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
{
|
||||
return $this->schema->used_in_object_classes
|
||||
->keys()
|
||||
->intersect($this->schema->heirachy($this->oc))
|
||||
->intersect($this->oc)
|
||||
->count() === 0;
|
||||
}
|
||||
|
||||
@ -329,9 +327,10 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
* @param bool $old Use old value
|
||||
* @param bool $new Enable adding values
|
||||
* @param bool $updated Has the entry been updated (uses rendering highlights))
|
||||
* @param Template|null $template
|
||||
* @return View
|
||||
*/
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
if ($this->is_internal)
|
||||
// @note Internal attributes cannot be edited
|
||||
@ -352,6 +351,7 @@ class Attribute implements \Countable, \ArrayAccess
|
||||
->with('edit',$edit)
|
||||
->with('old',$old)
|
||||
->with('new',$new)
|
||||
->with('template',$template)
|
||||
->with('updated',$updated);
|
||||
}
|
||||
|
||||
|
@ -5,7 +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\Classes\Template;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
/**
|
||||
@ -15,7 +15,7 @@ final class JpegPhoto extends Binary
|
||||
{
|
||||
use MD5Updates;
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
return view('components.attribute.binary.jpegphoto')
|
||||
->with('o',$this)
|
||||
|
@ -4,6 +4,7 @@ namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Traits\MD5Updates;
|
||||
@ -17,6 +18,15 @@ final class Certificate extends Attribute
|
||||
|
||||
private array $_object = [];
|
||||
|
||||
public function authority_key_identifier(int $key=0): string
|
||||
{
|
||||
$data = collect(explode("\n",$this->cert_info('extensions.authorityKeyIdentifier',$key)));
|
||||
return $data
|
||||
->filter(fn($item)=>Str::startsWith($item,'keyid:'))
|
||||
->map(fn($item)=>Str::after($item,'keyid:'))
|
||||
->first();
|
||||
}
|
||||
|
||||
public function certificate(int $key=0): string
|
||||
{
|
||||
return sprintf("-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----",
|
||||
@ -29,19 +39,23 @@ final class Certificate extends Attribute
|
||||
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
|
||||
public function expires(int $key=0): Carbon
|
||||
{
|
||||
return Carbon::createFromTimestampUTC($this->cert_info('validTo_time_t',$key));
|
||||
}
|
||||
|
||||
public function subject($key=0): string
|
||||
public function subject(int $key=0): string
|
||||
{
|
||||
$subject = collect($this->cert_info('subject',$key))->reverse();
|
||||
|
||||
return $subject->map(fn($item,$key)=>sprintf("%s=%s",$key,$item))->join(',');
|
||||
}
|
||||
|
||||
public function subject_key_identifier(int $key=0): string
|
||||
{
|
||||
return $this->cert_info('extensions.subjectKeyIdentifier',$key);
|
||||
}
|
||||
}
|
@ -2,9 +2,6 @@
|
||||
|
||||
namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
|
||||
@ -59,8 +58,6 @@ class Factory
|
||||
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($dn,$attribute,$values,$oc);
|
||||
}
|
||||
}
|
@ -5,14 +5,14 @@ namespace App\Classes\LDAP\Attribute\Internal;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Internal;
|
||||
use App\Ldap\Entry;
|
||||
use App\Classes\Template;
|
||||
|
||||
/**
|
||||
* Represents an attribute whose values are timestamps
|
||||
*/
|
||||
final class Timestamp extends Internal
|
||||
{
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
// @note Internal attributes cannot be edited
|
||||
return view('components.attribute.internal.timestamp')
|
||||
|
@ -5,7 +5,7 @@ namespace App\Classes\LDAP\Attribute;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Ldap\Entry;
|
||||
use App\Classes\Template;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
/**
|
||||
@ -17,7 +17,7 @@ final class KrbPrincipalKey extends Attribute
|
||||
|
||||
protected(set) bool $no_attr_tags = TRUE;
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
return view('components.attribute.krbprincipalkey')
|
||||
->with('o',$this)
|
||||
|
@ -6,7 +6,7 @@ use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Ldap\Entry;
|
||||
use App\Classes\Template;
|
||||
|
||||
/**
|
||||
* Represents an attribute whose value is a Kerberos Ticket Flag
|
||||
@ -50,7 +50,7 @@ final class KrbTicketFlags extends Attribute
|
||||
return $helpers;
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
return view('components.attribute.krbticketflags')
|
||||
->with('o',$this)
|
||||
|
@ -6,7 +6,7 @@ use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Ldap\Entry;
|
||||
use App\Classes\Template;
|
||||
|
||||
/**
|
||||
* Represents an ObjectClass Attribute
|
||||
@ -70,7 +70,7 @@ final class ObjectClass extends Attribute
|
||||
->contains($value);
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
return view('components.attribute.objectclass')
|
||||
->with('o',$this)
|
||||
|
@ -7,7 +7,7 @@ use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Ldap\Entry;
|
||||
use App\Classes\Template;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
/**
|
||||
@ -80,13 +80,14 @@ final class Password extends Attribute
|
||||
return ($helpers=static::helpers())->has($id) ? new ($helpers->get($id)) : NULL;
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
return view('components.attribute.password')
|
||||
->with('o',$this)
|
||||
->with('edit',$edit)
|
||||
->with('old',$old)
|
||||
->with('new',$new)
|
||||
->with('template',$template)
|
||||
->with('updated',$updated)
|
||||
->with('helpers',static::helpers()->map(fn($item,$key)=>['id'=>$key,'value'=>$key])->sort());
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Ldap\Entry;
|
||||
use App\Classes\Template;
|
||||
|
||||
/**
|
||||
* Represents the RDN for an Entry
|
||||
@ -35,7 +35,7 @@ final class RDN extends Attribute
|
||||
]);
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
return view('components.attribute.rdn')
|
||||
->with('o',$this);
|
||||
|
@ -2,12 +2,10 @@
|
||||
|
||||
namespace App\Classes\LDAP\Attribute;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
/**
|
||||
* Represents an attribute whose values are schema related
|
||||
@ -53,11 +51,4 @@ abstract class Schema extends Attribute
|
||||
$key,
|
||||
__('No description available, can you help with one?'));
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
{
|
||||
// @note Schema attributes cannot be edited
|
||||
return view('components.attribute.internal')
|
||||
->with('o',$this);
|
||||
}
|
||||
}
|
@ -5,14 +5,14 @@ namespace App\Classes\LDAP\Attribute\Schema;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Schema;
|
||||
use App\Ldap\Entry;
|
||||
use App\Classes\Template;
|
||||
|
||||
/**
|
||||
* Represents a Generic Schema Attribute
|
||||
*/
|
||||
class Generic extends Schema
|
||||
{
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
// @note Schema attributes cannot be edited
|
||||
return view('components.attribute.schema.generic')
|
||||
|
@ -5,7 +5,7 @@ namespace App\Classes\LDAP\Attribute\Schema;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Schema;
|
||||
use App\Ldap\Entry;
|
||||
use App\Classes\Template;
|
||||
|
||||
/**
|
||||
* Represents a Mechanisms Attribute
|
||||
@ -34,7 +34,7 @@ final class Mechanisms extends Schema
|
||||
return parent::_get(config_path('ldap_supported_saslmechanisms.txt'),$string,$key);
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
// @note Schema attributes cannot be edited
|
||||
return view('components.attribute.schema.mechanisms')
|
||||
|
@ -5,7 +5,7 @@ namespace App\Classes\LDAP\Attribute\Schema;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Schema;
|
||||
use App\Ldap\Entry;
|
||||
use App\Classes\Template;
|
||||
|
||||
/**
|
||||
* Represents an OID Attribute
|
||||
@ -35,7 +35,7 @@ final class OID extends Schema
|
||||
return parent::_get(config_path('ldap_supported_oids.txt'),$string,$key);
|
||||
}
|
||||
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE): View
|
||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL): View
|
||||
{
|
||||
// @note Schema attributes cannot be edited
|
||||
return view('components.attribute.schema.oid')
|
||||
|
@ -29,7 +29,7 @@ abstract class Export
|
||||
|
||||
abstract public function __toString(): string;
|
||||
|
||||
protected function header()
|
||||
protected function header(): string
|
||||
{
|
||||
$output = '';
|
||||
|
||||
@ -42,7 +42,7 @@ abstract class Export
|
||||
//$output .= sprintf('# %s: %s',__('Search Filter'),$this->entry->dn).$this->br;
|
||||
$output .= sprintf('# %s: %s',__('Total Entries'),$this->items->count()).$this->br;
|
||||
$output .= '#'.$this->br;
|
||||
$output .= sprintf('# %s %s (%s) on %s',__('Generated by'),config('app.name'),config('app.url'),date('F j, Y g:i a')).$this->br;
|
||||
$output .= sprintf('# %s %s (%s) on %s',__('Generated by'),config('app.name'),request()->root(),date('F j, Y g:i a')).$this->br;
|
||||
$output .= sprintf('# %s %s',__('Exported by'),Auth::user() ?: 'Anonymous').$this->br;
|
||||
$output .= sprintf('# %s: %s',__('Version'),config('app.version')).$this->br;
|
||||
|
||||
|
@ -7,304 +7,74 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Exceptions\InvalidUsage;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
/**
|
||||
* Represents an LDAP AttributeType
|
||||
*
|
||||
* @package phpLDAPadmin
|
||||
* @subpackage Schema
|
||||
*/
|
||||
final class AttributeType extends Base {
|
||||
// The attribute from which this attribute inherits (if any)
|
||||
private ?string $sup_attribute = NULL;
|
||||
final class AttributeType extends Base
|
||||
{
|
||||
private const LOGKEY = 'SAT';
|
||||
|
||||
// Array of AttributeTypes which inherit from this one
|
||||
private Collection $children;
|
||||
// An array of AttributeTypes which inherit from this one
|
||||
private(set) Collection $children;
|
||||
|
||||
// The equality rule used
|
||||
private ?string $equality = NULL;
|
||||
|
||||
// The ordering of the attributeType
|
||||
private ?string $ordering = NULL;
|
||||
|
||||
// Supports substring matching?
|
||||
private ?string $sub_str_rule = NULL;
|
||||
|
||||
// The full syntax string, ie 1.2.3.4{16}
|
||||
private ?string $syntax = NULL;
|
||||
private ?string $syntax_oid = NULL;
|
||||
|
||||
// boolean: is single valued only?
|
||||
private bool $is_single_value = FALSE;
|
||||
|
||||
// boolean: is collective?
|
||||
private bool $is_collective = FALSE;
|
||||
|
||||
// boolean: can use modify?
|
||||
private bool $is_no_user_modification = FALSE;
|
||||
|
||||
// The usage string set by the LDAP schema
|
||||
private ?string $usage = NULL;
|
||||
|
||||
// An array of alias attribute names, strings
|
||||
private Collection $aliases;
|
||||
|
||||
// The max number of characters this attribute can be
|
||||
private ?int $max_length = NULL;
|
||||
|
||||
// A string description of the syntax type (taken from the LDAPSyntaxes)
|
||||
/**
|
||||
* @deprecated - reference syntaxes directly if possible
|
||||
* @var string
|
||||
*/
|
||||
private ?string $type = NULL;
|
||||
|
||||
// An array of objectClasses which use this attributeType (must be set by caller)
|
||||
private Collection $used_in_object_classes;
|
||||
|
||||
// A list of object class names that require this attribute type.
|
||||
private Collection $required_by_object_classes;
|
||||
private(set) ?string $equality = NULL;
|
||||
|
||||
// This attribute has been forced a MAY attribute by the configuration.
|
||||
private bool $forced_as_may = FALSE;
|
||||
private(set) bool $forced_as_may = FALSE;
|
||||
|
||||
/**
|
||||
* Creates a new AttributeType object from a raw LDAP AttributeType string.
|
||||
*
|
||||
* eg: ( 2.5.4.0 NAME 'objectClass' DESC 'RFC4512: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
|
||||
*/
|
||||
public function __construct(string $line) {
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('Parsing AttributeType [%s]',$line));
|
||||
// boolean: is collective?
|
||||
private(set) bool $is_collective = FALSE;
|
||||
|
||||
parent::__construct($line);
|
||||
// Is this a must attribute
|
||||
private(set) bool $is_must = FALSE;
|
||||
|
||||
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||
// boolean: can use modify?
|
||||
private(set) bool $is_no_user_modification = FALSE;
|
||||
|
||||
// Init
|
||||
$this->children = collect();
|
||||
$this->aliases = collect();
|
||||
$this->used_in_object_classes = collect();
|
||||
$this->required_by_object_classes = collect();
|
||||
// boolean: is single valued only?
|
||||
private(set) bool $is_single_value = FALSE;
|
||||
|
||||
for ($i=0; $i < count($strings); $i++) {
|
||||
switch ($strings[$i]) {
|
||||
case '(':
|
||||
case ')':
|
||||
break;
|
||||
// The max number of characters this attribute can be
|
||||
private(set) ?int $max_length = NULL;
|
||||
|
||||
case 'NAME':
|
||||
// @note Some schema's return a (' instead of a ( '
|
||||
if ($strings[$i+1] != '(' && ! preg_match('/^\(/',$strings[$i+1])) {
|
||||
do {
|
||||
$this->name .= ($this->name ? ' ' : '').$strings[++$i];
|
||||
// An array of names (including aliases) that this attribute is known by
|
||||
private(set) Collection $names;
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
// The ordering of the attributeType
|
||||
private(set) ?string $ordering = NULL;
|
||||
|
||||
// This attribute has no aliases
|
||||
//$this->aliases = collect();
|
||||
// A list of object class names that require this attribute type.
|
||||
private(set) Collection $required_by_object_classes;
|
||||
|
||||
} else {
|
||||
$i++;
|
||||
// Which objectclass is defining this attribute for an Entry
|
||||
public ?string $source = NULL;
|
||||
|
||||
do {
|
||||
// In case we came here becaues of a ('
|
||||
if (preg_match('/^\(/',$strings[$i]))
|
||||
$strings[$i] = preg_replace('/^\(/','',$strings[$i]);
|
||||
else
|
||||
$i++;
|
||||
// Supports substring matching?
|
||||
private(set) ?string $sub_str_rule = NULL;
|
||||
|
||||
$this->name .= ($this->name ? ' ' : '').$strings[++$i];
|
||||
// The attribute from which this attribute inherits (if any)
|
||||
private(set) ?string $sup_attribute = NULL;
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
// The full syntax string, ie 1.2.3.4{16}
|
||||
private(set) ?string $syntax = NULL;
|
||||
private(set) ?string $syntax_oid = NULL;
|
||||
|
||||
// Add alias names for this attribute
|
||||
while ($strings[++$i] != ')') {
|
||||
$alias = $strings[$i];
|
||||
$alias = preg_replace("/^\'(.*)\'$/",'$1',$alias);
|
||||
$this->addAlias($alias);
|
||||
}
|
||||
}
|
||||
// The usage string set by the LDAP schema
|
||||
private(set) ?string $usage = NULL;
|
||||
|
||||
$this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case NAME returned (%s)',$this->name),['aliases'=>$this->aliases]);
|
||||
break;
|
||||
|
||||
case 'DESC':
|
||||
do {
|
||||
$this->description .= ($this->description ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||
break;
|
||||
|
||||
case 'OBSOLETE':
|
||||
$this->is_obsolete = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete));
|
||||
break;
|
||||
|
||||
case 'SUP':
|
||||
$i++;
|
||||
$this->sup_attribute = preg_replace("/^\'(.*)\'$/",'$1',$strings[$i]);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_attribute));
|
||||
break;
|
||||
|
||||
case 'EQUALITY':
|
||||
$this->equality = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case EQUALITY returned (%s)',$this->equality));
|
||||
break;
|
||||
|
||||
case 'ORDERING':
|
||||
$this->ordering = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case ORDERING returned (%s)',$this->ordering));
|
||||
break;
|
||||
|
||||
case 'SUBSTR':
|
||||
$this->sub_str_rule = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case SUBSTR returned (%s)',$this->sub_str_rule));
|
||||
break;
|
||||
|
||||
case 'SYNTAX':
|
||||
$this->syntax = $strings[++$i];
|
||||
$this->syntax_oid = preg_replace('/{\d+}$/','',$this->syntax);
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('/ Evaluating SYNTAX returned (%s) [%s]',$this->syntax,$this->syntax_oid));
|
||||
|
||||
// Does this SYNTAX string specify a max length (ie, 1.2.3.4{16})
|
||||
$m = [];
|
||||
if (preg_match('/{(\d+)}$/',$this->syntax,$m))
|
||||
$this->max_length = $m[1];
|
||||
else
|
||||
$this->max_length = NULL;
|
||||
|
||||
if ($i < count($strings) - 1 && $strings[$i+1] == '{')
|
||||
do {
|
||||
$this->name .= ' '.$strings[++$i];
|
||||
} while ($strings[$i] != '}');
|
||||
|
||||
$this->syntax = preg_replace("/^\'(.*)\'$/",'$1',$this->syntax);
|
||||
$this->syntax_oid = preg_replace("/^\'(.*)\'$/",'$1',$this->syntax_oid);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case SYNTAX returned (%s) [%s] {%d}',$this->syntax,$this->syntax_oid,$this->max_length));
|
||||
break;
|
||||
|
||||
case 'SINGLE-VALUE':
|
||||
$this->is_single_value = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case SINGLE-VALUE returned (%s)',$this->is_single_value));
|
||||
break;
|
||||
|
||||
case 'COLLECTIVE':
|
||||
$this->is_collective = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case COLLECTIVE returned (%s)',$this->is_collective));
|
||||
break;
|
||||
|
||||
case 'NO-USER-MODIFICATION':
|
||||
$this->is_no_user_modification = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case NO-USER-MODIFICATION returned (%s)',$this->is_no_user_modification));
|
||||
break;
|
||||
|
||||
case 'USAGE':
|
||||
$this->usage = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case USAGE returned (%s)',$this->usage));
|
||||
break;
|
||||
|
||||
// @note currently not captured
|
||||
case 'X-ORDERED':
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::error(sprintf('- Case X-ORDERED returned (%s)',$strings[++$i]));
|
||||
break;
|
||||
|
||||
// @note currently not captured
|
||||
case 'X-ORIGIN':
|
||||
$value = '';
|
||||
|
||||
do {
|
||||
$value .= ($value ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::error(sprintf('- Case X-ORIGIN returned (%s)',$value));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||
$this->oid = $strings[$i];
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||
|
||||
} elseif ($strings[$i])
|
||||
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
// When we clone, we need to break the reference too
|
||||
$this->aliases = clone $this->aliases;
|
||||
}
|
||||
// An array of objectClasses which use this attributeType (must be set by caller)
|
||||
private(set) Collection $used_in_object_classes;
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
switch ($key) {
|
||||
case 'aliases': return $this->aliases;
|
||||
case 'children': return $this->children;
|
||||
case 'forced_as_may': return $this->forced_as_may;
|
||||
case 'is_collective': return $this->is_collective;
|
||||
case 'is_editable': return ! $this->is_no_user_modification;
|
||||
case 'is_no_user_modification': return $this->is_no_user_modification;
|
||||
case 'is_single_value': return $this->is_single_value;
|
||||
case 'equality': return $this->equality;
|
||||
case 'max_length': return $this->max_length;
|
||||
case 'ordering': return $this->ordering;
|
||||
case 'required_by_object_classes': return $this->required_by_object_classes;
|
||||
case 'sub_str_rule': return $this->sub_str_rule;
|
||||
case 'sup_attribute': return $this->sup_attribute;
|
||||
case 'syntax': return $this->syntax;
|
||||
case 'syntax_oid': return $this->syntax_oid;
|
||||
case 'type': return $this->type;
|
||||
case 'usage': return $this->usage;
|
||||
case 'used_in_object_classes': return $this->used_in_object_classes;
|
||||
|
||||
default: return parent::__get($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute name to the alias array.
|
||||
*
|
||||
* @param string $alias The name of a new attribute to add to this attribute's list of aliases.
|
||||
*/
|
||||
public function addAlias(string $alias): void
|
||||
{
|
||||
$this->aliases->push($alias);
|
||||
return match ($key) {
|
||||
'names_lc' => $this->names->map('strtolower'),
|
||||
default => parent::__get($key)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,7 +85,8 @@ final class AttributeType extends Base {
|
||||
*/
|
||||
public function addChild(string $child): void
|
||||
{
|
||||
$this->children->push($child);
|
||||
$this->children
|
||||
->push($child);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -346,144 +117,10 @@ final class AttributeType extends Base {
|
||||
|
||||
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).
|
||||
*
|
||||
* @return Collection An array of names of attributes which alias this attribute or
|
||||
* an empty array if no attribute aliases this object.
|
||||
* @deprecated use class->aliases
|
||||
*/
|
||||
public function getAliases(): Collection
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this attribute's equality string
|
||||
*
|
||||
* @return string
|
||||
* @deprecated use $this->equality
|
||||
*/
|
||||
public function getEquality()
|
||||
{
|
||||
return $this->equality;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this attribute is collective.
|
||||
*
|
||||
* @return boolean Returns TRUE if this attribute is collective and FALSE otherwise.
|
||||
* @deprecated use $this->is_collective
|
||||
*/
|
||||
public function getIsCollective(): bool
|
||||
{
|
||||
return $this->is_collective;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this attribute is not modifiable by users.
|
||||
*
|
||||
* @return boolean Returns TRUE if this attribute is not modifiable by users.
|
||||
* @deprecated use $this->is_no_user_modification
|
||||
*/
|
||||
public function getIsNoUserModification(): bool
|
||||
{
|
||||
return $this->is_no_user_modification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this attribute is single-valued. If this attribute only supports single values, TRUE
|
||||
* is returned. If this attribute supports multiple values, FALSE is returned.
|
||||
*
|
||||
* @return boolean Returns TRUE if this attribute is single-valued or FALSE otherwise.
|
||||
* @deprecated use class->is_single_value
|
||||
*/
|
||||
public function getIsSingleValue(): bool
|
||||
{
|
||||
return $this->is_single_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this attribute's the maximum length. If no maximum is defined by the LDAP server, NULL is returned.
|
||||
*
|
||||
* @return int The maximum length (in characters) of this attribute or NULL if no maximum is specified.
|
||||
* @deprecated use $this->max_length;
|
||||
*/
|
||||
public function getMaxLength()
|
||||
{
|
||||
return $this->max_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this attribute's ordering specification.
|
||||
*
|
||||
* @return string
|
||||
* @deprecated use $this->ordering
|
||||
*/
|
||||
public function getOrdering(): string
|
||||
{
|
||||
return $this->ordering;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this attribute's substring matching specification
|
||||
*
|
||||
* @return string
|
||||
* @deprecated use $this->sub_str_rule;
|
||||
*/
|
||||
public function getSubstr() {
|
||||
return $this->sub_str_rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this attribute's parent attribute (if any). If this attribute does not
|
||||
* inherit from another attribute, NULL is returned.
|
||||
*
|
||||
* @return string
|
||||
* @deprecated use $class->sup_attribute directly
|
||||
*/
|
||||
public function getSupAttribute() {
|
||||
return $this->sup_attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this attribute's syntax OID. Differs from getSyntaxString() in that this
|
||||
* function only returns the actual OID with any length specification removed.
|
||||
* Ie, if the syntax string is "1.2.3.4{16}", this function only retruns
|
||||
* "1.2.3.4".
|
||||
*
|
||||
* @return string The syntax OID string.
|
||||
* @deprecated use $this->syntax_oid;
|
||||
*/
|
||||
public function getSyntaxOID()
|
||||
{
|
||||
return $this->syntax_oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this attribute's usage string as defined by the LDAP server
|
||||
*
|
||||
* @return string
|
||||
* @deprecated use $this->usage
|
||||
*/
|
||||
public function getUsage()
|
||||
{
|
||||
return $this->usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of "used in" objectClasses, that is the list of objectClasses
|
||||
* which provide this attribute.
|
||||
*
|
||||
* @return Collection An array of names of objectclasses (strings) which provide this attribute
|
||||
* @deprecated use $this->used_in_object_classes
|
||||
*/
|
||||
public function getUsedInObjectClasses(): Collection
|
||||
{
|
||||
return $this->used_in_object_classes;
|
||||
return Attribute\Factory::create(
|
||||
dn:'',
|
||||
attribute:$this->name,
|
||||
values:[]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -492,58 +129,162 @@ final class AttributeType extends Base {
|
||||
* @param Collection $ocs
|
||||
* @return Collection
|
||||
*/
|
||||
public function heirachy(Collection $ocs): Collection
|
||||
private function heirachy(Collection $ocs): Collection
|
||||
{
|
||||
$result = collect();
|
||||
|
||||
foreach ($ocs as $oc) {
|
||||
$schema = config('server')
|
||||
->schema('objectclasses',$oc)
|
||||
->getParents(TRUE)
|
||||
->pluck('name');
|
||||
$item = config('server')
|
||||
->schema('objectclasses',$oc);
|
||||
|
||||
$result = $result->merge($schema)->push($oc);
|
||||
$result = $result
|
||||
->merge($item
|
||||
->getParents(TRUE)
|
||||
->pluck('oid'))
|
||||
->push($item->oid);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @deprecated use $this->forced_as_may
|
||||
*/
|
||||
public function isForceMay(): bool
|
||||
{
|
||||
return $this->forced_as_may;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an attribute name from this attribute's alias array.
|
||||
* Creates a new AttributeType object from a raw LDAP AttributeType string.
|
||||
*
|
||||
* @param string $alias The name of the attribute to remove.
|
||||
* eg: ( 2.5.4.0 NAME 'objectClass' DESC 'RFC4512: object classes of the entity' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
|
||||
*/
|
||||
public function removeAlias(string $alias): void
|
||||
protected function parse(string $line): void
|
||||
{
|
||||
if (($x=$this->aliases->search($alias)) !== FALSE)
|
||||
$this->aliases->forget($x);
|
||||
Log::debug(sprintf('%s:Parsing AttributeType [%s]',self::LOGKEY,$line));
|
||||
|
||||
// Init
|
||||
$this->names = collect();
|
||||
$this->children = collect();
|
||||
$this->used_in_object_classes = collect();
|
||||
$this->required_by_object_classes = collect();
|
||||
|
||||
parent::parse($line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this attribute's list of aliases.
|
||||
*
|
||||
* @param Collection $aliases The array of alias names (strings)
|
||||
* @deprecated use $this->aliases =
|
||||
*/
|
||||
public function setAliases(Collection $aliases): void
|
||||
protected function parse_chunk(array $strings,int &$i): void
|
||||
{
|
||||
$this->aliases = $aliases;
|
||||
}
|
||||
switch ($strings[$i]) {
|
||||
case 'NAME':
|
||||
$name = '';
|
||||
|
||||
/**
|
||||
* This function will mark this attribute as a forced MAY attribute
|
||||
*/
|
||||
public function setForceMay() {
|
||||
$this->forced_as_may = TRUE;
|
||||
// @note Some schema's return a (' instead of a ( '
|
||||
// @note This attribute format has no aliases
|
||||
if ($strings[$i+1] !== '(' && ! preg_match('/^\(/',$strings[$i+1])) {
|
||||
do {
|
||||
$name .= ($name ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
} else {
|
||||
$i++;
|
||||
|
||||
do {
|
||||
// In case we came here because of a ('
|
||||
if (preg_match('/^\(/',$strings[$i]))
|
||||
$strings[$i] = preg_replace('/^\(/','',$strings[$i]);
|
||||
else
|
||||
$i++;
|
||||
|
||||
$name .= ($name ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
// Add alias names for this attribute
|
||||
while ($strings[++$i] !== ')') {
|
||||
$alias = preg_replace("/^\'(.*)\'$/",'$1',$strings[$i]);
|
||||
$this->names->push($alias);
|
||||
}
|
||||
}
|
||||
|
||||
$this->names = $this->names->push(preg_replace("/^\'(.*)\'$/",'$1',$name))->sort();
|
||||
$this->forced_as_may = $this->names_lc
|
||||
->intersect(array_map('strtolower',config('pla.force_may',[])))
|
||||
->count() > 0;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case NAME returned (%s)',self::LOGKEY,$this->name),['names'=>$this->names]);
|
||||
break;
|
||||
|
||||
case 'SUP':
|
||||
$this->sup_attribute = preg_replace("/^\'(.*)\'$/",'$1',$strings[++$i]);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case SUP returned (%s)',self::LOGKEY,$this->sup_attribute));
|
||||
break;
|
||||
|
||||
case 'EQUALITY':
|
||||
$this->equality = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case EQUALITY returned (%s)',self::LOGKEY,$this->equality));
|
||||
break;
|
||||
|
||||
case 'ORDERING':
|
||||
$this->ordering = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case ORDERING returned (%s)',self::LOGKEY,$this->ordering));
|
||||
break;
|
||||
|
||||
case 'SUBSTR':
|
||||
$this->sub_str_rule = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case SUBSTR returned (%s)',self::LOGKEY,$this->sub_str_rule));
|
||||
break;
|
||||
|
||||
case 'SYNTAX':
|
||||
$this->syntax = preg_replace("/^\'(.*)\'$/",'$1',$strings[++$i]);
|
||||
$this->syntax_oid = preg_replace("/^\'?(.*){\d+}\'?$/",'$1',$this->syntax);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:/ Evaluating SYNTAX returned (%s) [%s]',self::LOGKEY,$this->syntax,$this->syntax_oid));
|
||||
|
||||
// Does this SYNTAX string specify a max length (ie, 1.2.3.4{16})
|
||||
$m = [];
|
||||
$this->max_length = preg_match('/{(\d+)}$/',$this->syntax,$m)
|
||||
? $m[1]
|
||||
: NULL;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case SYNTAX returned (%s) [%s] {%d}',self::LOGKEY,$this->syntax,$this->syntax_oid,$this->max_length));
|
||||
break;
|
||||
|
||||
case 'SINGLE-VALUE':
|
||||
$this->is_single_value = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case SINGLE-VALUE returned (%s)',self::LOGKEY,$this->is_single_value));
|
||||
break;
|
||||
|
||||
case 'COLLECTIVE':
|
||||
$this->is_collective = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case COLLECTIVE returned (%s)',self::LOGKEY,$this->is_collective));
|
||||
break;
|
||||
|
||||
case 'NO-USER-MODIFICATION':
|
||||
$this->is_no_user_modification = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case NO-USER-MODIFICATION returned (%s)',self::LOGKEY,$this->is_no_user_modification));
|
||||
break;
|
||||
|
||||
case 'USAGE':
|
||||
$this->usage = $strings[++$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case USAGE returned (%s)',self::LOGKEY,$this->usage));
|
||||
break;
|
||||
|
||||
default:
|
||||
parent::parse_chunk($strings,$i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -557,13 +298,28 @@ final class AttributeType extends Base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this attribute's SUP attribute (ie, the attribute from which this attribute inherits).
|
||||
* If this is a MUST attribute to the objectclass that defines it
|
||||
*
|
||||
* @param string $attr The name of the new parent (SUP) attribute
|
||||
* @return void
|
||||
*/
|
||||
public function setSupAttribute(string $attr): void
|
||||
public function setMust(): void
|
||||
{
|
||||
$this->sup_attribute = trim($attr);
|
||||
$this->is_must = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this attribute's name.
|
||||
*
|
||||
* @param string $name The new name to give this attribute.
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function setName(string $name): void
|
||||
{
|
||||
// Quick validation
|
||||
if ($this->names_lc->count() && (! $this->names_lc->contains(strtolower($name))))
|
||||
throw new InvalidUsage(sprintf('Cannot set attribute name to [%s], its not an alias for [%s]',$name,$this->names->join(',')));
|
||||
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Classes\LDAP\Schema;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Exceptions\InvalidUsage;
|
||||
|
||||
/**
|
||||
@ -10,38 +12,38 @@ use App\Exceptions\InvalidUsage;
|
||||
* A schema item is an ObjectClass, an AttributeBype, a MatchingRule, or a Syntax.
|
||||
* All schema items have at least two things in common: An OID and a Description.
|
||||
*/
|
||||
abstract class Base {
|
||||
abstract class Base
|
||||
{
|
||||
private const LOGKEY = 'Sb-';
|
||||
|
||||
protected const DEBUG_VERBOSE = FALSE;
|
||||
|
||||
// Record the LDAP String
|
||||
private string $line;
|
||||
private(set) string $line;
|
||||
|
||||
// The schema item's name.
|
||||
protected string $name = '';
|
||||
protected(set) string $name = '';
|
||||
|
||||
// The OID of this schema item.
|
||||
protected string $oid = '';
|
||||
protected(set) string $oid = '';
|
||||
|
||||
# The description of this schema item.
|
||||
protected string $description = '';
|
||||
protected(set) string $description = '';
|
||||
|
||||
// Boolean value indicating whether this objectClass is obsolete
|
||||
private bool $is_obsolete = FALSE;
|
||||
private(set) bool $is_obsolete = FALSE;
|
||||
|
||||
public function __construct(string $line)
|
||||
{
|
||||
$this->line = $line;
|
||||
|
||||
$this->parse($line);
|
||||
}
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
switch ($key) {
|
||||
case 'description': return $this->description;
|
||||
case 'is_obsolete': return $this->is_obsolete;
|
||||
case 'line': return $this->line;
|
||||
case 'name': return $this->name;
|
||||
case 'name_lc': return strtolower($this->name);
|
||||
case 'oid': return $this->oid;
|
||||
|
||||
default:
|
||||
throw new InvalidUsage('Unknown key:'.$key);
|
||||
@ -54,69 +56,95 @@ abstract class Base {
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @deprecated replace with $class->description
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this item is flagged as obsolete by the LDAP server.
|
||||
*
|
||||
* @deprecated replace with $this->is_obsolete
|
||||
*/
|
||||
public function getIsObsolete(): bool
|
||||
{
|
||||
return $this->is_obsolete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the objects name.
|
||||
*
|
||||
* @param boolean $lower Return the name in lower case (default)
|
||||
* @return string The name
|
||||
* @deprecated use object->name
|
||||
*/
|
||||
public function getName(bool $lower=TRUE): string
|
||||
{
|
||||
return $lower ? strtolower($this->name) : $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the objects name.
|
||||
*
|
||||
* @return string The name
|
||||
* @deprecated use object->oid
|
||||
*/
|
||||
public function getOID(): string
|
||||
{
|
||||
return $this->oid;
|
||||
}
|
||||
|
||||
public function setDescription(string $desc): void
|
||||
protected function parse(string $line): void
|
||||
{
|
||||
$this->description = $desc;
|
||||
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
for ($i=0; $i < count($strings); $i++) {
|
||||
$this->parse_chunk($strings,$i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this attribute's name.
|
||||
*
|
||||
* @param string $name The new name to give this attribute.
|
||||
*/
|
||||
public function setName($name): void
|
||||
protected function parse_chunk(array $strings,int &$i): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
switch ($strings[$i]) {
|
||||
case '(':
|
||||
case ')':
|
||||
break;
|
||||
|
||||
public function setOID(string $oid): void
|
||||
{
|
||||
$this->oid = $oid;
|
||||
case 'NAME':
|
||||
if ($strings[$i+1] !== '(') {
|
||||
do {
|
||||
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||
} while (! preg_match('/\'$/s',$strings[$i]));
|
||||
|
||||
} else {
|
||||
$i++;
|
||||
|
||||
do {
|
||||
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||
} while (! preg_match('/\'$/s',$strings[$i]));
|
||||
|
||||
do {
|
||||
$i++;
|
||||
} while (! preg_match('/\)+\)?/',$strings[$i]));
|
||||
}
|
||||
|
||||
$this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case NAME returned (%s)',self::LOGKEY,$this->name));
|
||||
break;
|
||||
|
||||
case 'DESC':
|
||||
do {
|
||||
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match('/\'$/s',$strings[$i]));
|
||||
|
||||
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case DESC returned (%s)',self::LOGKEY,$this->description));
|
||||
break;
|
||||
|
||||
case 'OBSOLETE':
|
||||
$this->is_obsolete = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case OBSOLETE returned (%s)',self::LOGKEY,$this->is_obsolete));
|
||||
break;
|
||||
|
||||
// @note currently not captured
|
||||
case 'X-SUBST':
|
||||
case 'X-ORDERED':
|
||||
case 'X-EQUALITY':
|
||||
case 'X-ORIGIN':
|
||||
$value = '';
|
||||
|
||||
do {
|
||||
$value .= ($value ? ' ' : '').preg_replace('/^\'(.+)\'$/','$1',$strings[++$i]);
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case [%s] returned (%s) - IGNORED',self::LOGKEY,$strings[$i],$value));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||
$this->oid = $strings[$i];
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case default returned OID (%s)',self::LOGKEY,$this->oid));
|
||||
|
||||
} elseif ($strings[$i])
|
||||
Log::alert(sprintf('%s:! Case default discovered a value NOT parsed (%s)',self::LOGKEY,$strings[$i]));
|
||||
}
|
||||
}
|
||||
}
|
@ -6,74 +6,49 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Represents an LDAP Syntax
|
||||
*
|
||||
* @package phpLDAPadmin
|
||||
* @subpackage Schema
|
||||
*/
|
||||
final class LDAPSyntax extends Base {
|
||||
final class LDAPSyntax extends Base
|
||||
{
|
||||
private const LOGKEY = 'SLS';
|
||||
|
||||
// Is human readable?
|
||||
private ?bool $is_not_human_readable = NULL;
|
||||
private(set) ?bool $is_not_human_readable = NULL;
|
||||
|
||||
// Binary transfer required?
|
||||
private ?bool $binary_transfer_required = NULL;
|
||||
private(set) ?bool $binary_transfer_required = NULL;
|
||||
|
||||
/**
|
||||
* Creates a new Syntax object from a raw LDAP syntax string.
|
||||
*/
|
||||
public function __construct(string $line) {
|
||||
Log::debug(sprintf('Parsing LDAPSyntax [%s]',$line));
|
||||
protected function parse(string $line): void
|
||||
{
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:Parsing LDAPSyntax [%s]',self::LOGKEY,$line));
|
||||
|
||||
parent::__construct($line);
|
||||
|
||||
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||
parent::parse($line);
|
||||
}
|
||||
|
||||
protected function parse_chunk(array $strings,int &$i): void
|
||||
{
|
||||
for ($i=0; $i<count($strings); $i++) {
|
||||
switch($strings[$i]) {
|
||||
case '(':
|
||||
case ')':
|
||||
break;
|
||||
|
||||
case 'DESC':
|
||||
do {
|
||||
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||
|
||||
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||
break;
|
||||
|
||||
case 'X-BINARY-TRANSFER-REQUIRED':
|
||||
$this->binary_transfer_required = (str_replace("'",'',$strings[++$i]) === 'TRUE');
|
||||
|
||||
Log::debug(sprintf('- Case X-BINARY-TRANSFER-REQUIRED returned (%s)',$this->binary_transfer_required));
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case X-BINARY-TRANSFER-REQUIRED returned (%s)',self::LOGKEY,$this->binary_transfer_required));
|
||||
break;
|
||||
|
||||
case 'X-NOT-HUMAN-READABLE':
|
||||
$this->is_not_human_readable = (str_replace("'",'',$strings[++$i]) === 'TRUE');
|
||||
|
||||
Log::debug(sprintf('- Case X-NOT-HUMAN-READABLE returned (%s)',$this->is_not_human_readable));
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case X-NOT-HUMAN-READABLE returned (%s)',self::LOGKEY,$this->is_not_human_readable));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||
$this->oid = $strings[$i];
|
||||
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||
|
||||
} elseif ($strings[$i])
|
||||
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||
parent::parse_chunk($strings,$i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
switch ($key) {
|
||||
case 'binary_transfer_required': return $this->binary_transfer_required;
|
||||
case 'is_not_human_readable': return $this->is_not_human_readable;
|
||||
|
||||
default: return parent::__get($key);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,106 +7,16 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Represents an LDAP MatchingRule
|
||||
*
|
||||
* @package phpLDAPadmin
|
||||
* @subpackage Schema
|
||||
*/
|
||||
final class MatchingRule extends Base {
|
||||
final class MatchingRule extends Base
|
||||
{
|
||||
private const LOGKEY = 'SMR';
|
||||
|
||||
// This rule's syntax OID
|
||||
private ?string $syntax = NULL;
|
||||
private(set) ?string $syntax = NULL;
|
||||
|
||||
// An array of attribute names who use this MatchingRule
|
||||
private Collection $used_by_attrs;
|
||||
|
||||
/**
|
||||
* Creates a new MatchingRule object from a raw LDAP MatchingRule string.
|
||||
*/
|
||||
function __construct(string $line) {
|
||||
Log::debug(sprintf('Parsing MatchingRule [%s]',$line));
|
||||
|
||||
parent::__construct($line);
|
||||
|
||||
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
// Init
|
||||
$this->used_by_attrs = collect();
|
||||
|
||||
for ($i=0; $i<count($strings); $i++) {
|
||||
switch ($strings[$i]) {
|
||||
case '(':
|
||||
case ')':
|
||||
break;
|
||||
|
||||
case 'NAME':
|
||||
if ($strings[$i+1] != '(') {
|
||||
do {
|
||||
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
} else {
|
||||
$i++;
|
||||
|
||||
do {
|
||||
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
do {
|
||||
$i++;
|
||||
|
||||
} while (! preg_match('/\)+\)?/',$strings[$i]));
|
||||
}
|
||||
|
||||
$this->name = preg_replace("/^\'/",'',$this->name);
|
||||
$this->name = preg_replace("/\'$/",'',$this->name);
|
||||
|
||||
Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name)));
|
||||
break;
|
||||
|
||||
case 'DESC':
|
||||
do {
|
||||
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||
|
||||
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||
break;
|
||||
|
||||
case 'OBSOLETE':
|
||||
$this->is_obsolete = TRUE;
|
||||
|
||||
Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete));
|
||||
break;
|
||||
|
||||
case 'SYNTAX':
|
||||
$this->syntax = $strings[++$i];
|
||||
|
||||
Log::debug(sprintf('- Case SYNTAX returned (%s)',$this->syntax));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||
$this->oid = $strings[$i];
|
||||
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||
|
||||
} elseif ($strings[$i])
|
||||
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
switch ($key) {
|
||||
case 'syntax': return $this->syntax;
|
||||
case 'used_by_attrs': return $this->used_by_attrs;
|
||||
|
||||
default: return parent::__get($key);
|
||||
}
|
||||
}
|
||||
private(set) Collection $used_by_attrs;
|
||||
|
||||
/**
|
||||
* Adds an attribute name to the list of attributes who use this MatchingRule
|
||||
@ -120,23 +30,33 @@ final class MatchingRule extends Base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of attribute names (strings) which use this MatchingRule
|
||||
* Creates a new MatchingRule object from a raw LDAP MatchingRule string.
|
||||
*
|
||||
* @return array The array of attribute names (strings).
|
||||
* @deprecated use $this->used_by_attrs
|
||||
* @param string $line
|
||||
* @return void
|
||||
*/
|
||||
public function getUsedByAttrs()
|
||||
protected function parse(string $line): void
|
||||
{
|
||||
return $this->used_by_attrs;
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:Parsing MatchingRule [%s]',self::LOGKEY,$line));
|
||||
|
||||
// Init
|
||||
$this->used_by_attrs = collect();
|
||||
|
||||
parent::parse($line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of used_by_attrs to the array specified by $attrs;
|
||||
*
|
||||
* @param Collection $attrs The array of attribute names (strings) which use this MatchingRule
|
||||
*/
|
||||
public function setUsedByAttrs(Collection $attrs): void
|
||||
protected function parse_chunk(array $strings,int &$i): void
|
||||
{
|
||||
$this->used_by_attrs = $attrs;
|
||||
switch ($strings[$i]) {
|
||||
case 'SYNTAX':
|
||||
$this->syntax = $strings[++$i];
|
||||
|
||||
Log::debug(sprintf('- Case SYNTAX returned (%s)',$this->syntax));
|
||||
break;
|
||||
|
||||
default:
|
||||
parent::parse_chunk($strings,$i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,99 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Schema;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Represents an LDAP schema matchingRuleUse entry
|
||||
*
|
||||
* @package phpLDAPadmin
|
||||
* @subpackage Schema
|
||||
*/
|
||||
final class MatchingRuleUse extends Base {
|
||||
// An array of attribute names who use this MatchingRule
|
||||
private Collection $used_by_attrs;
|
||||
|
||||
function __construct(string $line) {
|
||||
Log::debug(sprintf('Parsing MatchingRuleUse [%s]',$line));
|
||||
|
||||
parent::__construct($line);
|
||||
|
||||
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
// Init
|
||||
$this->used_by_attrs = collect();
|
||||
|
||||
for ($i=0; $i<count($strings); $i++) {
|
||||
switch ($strings[$i]) {
|
||||
case '(':
|
||||
case ')':
|
||||
break;
|
||||
|
||||
case 'NAME':
|
||||
if ($strings[$i+1] != '(') {
|
||||
do {
|
||||
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
} else {
|
||||
$i++;
|
||||
|
||||
do {
|
||||
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match("/\'$/s",$strings[$i]));
|
||||
|
||||
do {
|
||||
$i++;
|
||||
|
||||
} while (! preg_match('/\)+\)?/',$strings[$i]));
|
||||
}
|
||||
|
||||
$this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name);
|
||||
|
||||
Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name)));
|
||||
break;
|
||||
|
||||
case 'APPLIES':
|
||||
if ($strings[$i+1] != '(') {
|
||||
// Has a single attribute name
|
||||
$this->used_by_attrs = collect($strings[++$i]);
|
||||
|
||||
} else {
|
||||
// Has multiple attribute names
|
||||
while ($strings[++$i] != ')') {
|
||||
$new_attr = $strings[++$i];
|
||||
$new_attr = preg_replace("/^\'(.*)\'$/",'$1',$new_attr);
|
||||
|
||||
$this->used_by_attrs->push($new_attr);
|
||||
}
|
||||
}
|
||||
|
||||
Log::debug(sprintf('- Case APPLIES returned (%s)',$this->used_by_attrs->join(',')));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||
$this->oid = $strings[$i];
|
||||
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||
|
||||
} elseif ($strings[$i])
|
||||
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of attribute names (strings) which use this MatchingRuleUse object.
|
||||
*
|
||||
* @return array The array of attribute names (strings).
|
||||
* @deprecated use $this->used_by_attrs
|
||||
*/
|
||||
public function getUsedByAttrs()
|
||||
{
|
||||
return $this->used_by_attrs;
|
||||
}
|
||||
}
|
@ -10,206 +10,28 @@ use App\Exceptions\InvalidUsage;
|
||||
|
||||
/**
|
||||
* Represents an LDAP Schema objectClass
|
||||
*
|
||||
* @package phpLDAPadmin
|
||||
* @subpackage Schema
|
||||
*/
|
||||
final class ObjectClass extends Base
|
||||
{
|
||||
private const LOGKEY = 'SOC';
|
||||
|
||||
// Array of objectClasses which inherit from this one
|
||||
private(set) Collection $child_classes;
|
||||
|
||||
// Array of objectClass names from which this objectClass inherits
|
||||
private Collection $sup_classes;
|
||||
private(set) Collection $sup_classes;
|
||||
|
||||
// One of STRUCTURAL, ABSTRACT, or AUXILIARY
|
||||
private int $type;
|
||||
|
||||
// Arrays of attribute names that this objectClass requires
|
||||
private Collection $must_attrs;
|
||||
|
||||
// Arrays of attribute names that this objectClass allows, but does not require
|
||||
private Collection $may_attrs;
|
||||
|
||||
// Arrays of attribute names that this objectClass has been forced to MAY attrs, due to configuration
|
||||
private Collection $may_force;
|
||||
|
||||
// Array of objectClasses which inherit from this one
|
||||
private Collection $child_objectclasses;
|
||||
|
||||
private bool $is_obsolete;
|
||||
|
||||
/**
|
||||
* Creates a new ObjectClass object given a raw LDAP objectClass string.
|
||||
*
|
||||
* eg: ( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRACT MUST objectClass )
|
||||
*
|
||||
* @param string $line Schema Line
|
||||
* @param Server $server
|
||||
* @todo Deprecate this $server variable? It is only used for isForceMay() determination, and that might be better done elsewhere?
|
||||
*/
|
||||
public function __construct(string $line,Server $server)
|
||||
{
|
||||
parent::__construct($line);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('Parsing ObjectClass [%s]',$line));
|
||||
|
||||
$strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
// Init
|
||||
$this->may_attrs = collect();
|
||||
$this->may_force = collect();
|
||||
$this->must_attrs = collect();
|
||||
$this->sup_classes = collect();
|
||||
$this->child_objectclasses = collect();
|
||||
|
||||
for ($i=0; $i < count($strings); $i++) {
|
||||
switch ($strings[$i]) {
|
||||
case '(':
|
||||
case ')':
|
||||
break;
|
||||
|
||||
case 'NAME':
|
||||
if ($strings[$i+1] != '(') {
|
||||
do {
|
||||
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match('/\'$/s',$strings[$i]));
|
||||
|
||||
} else {
|
||||
$i++;
|
||||
|
||||
do {
|
||||
$this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match('/\'$/s',$strings[$i]));
|
||||
|
||||
do {
|
||||
$i++;
|
||||
} while (! preg_match('/\)+\)?/',$strings[$i]));
|
||||
}
|
||||
|
||||
$this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name)));
|
||||
break;
|
||||
|
||||
case 'DESC':
|
||||
do {
|
||||
$this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i];
|
||||
|
||||
} while (! preg_match('/\'$/s',$strings[$i]));
|
||||
|
||||
$this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case DESC returned (%s)',$this->description));
|
||||
break;
|
||||
|
||||
case 'OBSOLETE':
|
||||
$this->is_obsolete = TRUE;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete));
|
||||
break;
|
||||
|
||||
case 'SUP':
|
||||
if ($strings[$i+1] != '(') {
|
||||
$this->sup_classes->push(preg_replace("/'/",'',$strings[++$i]));
|
||||
|
||||
} else {
|
||||
$i++;
|
||||
|
||||
do {
|
||||
$i++;
|
||||
|
||||
if ($strings[$i] != '$')
|
||||
$this->sup_classes->push(preg_replace("/'/",'',$strings[$i]));
|
||||
|
||||
} while (! preg_match('/\)+\)?/',$strings[$i+1]));
|
||||
}
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_classes->join(',')));
|
||||
break;
|
||||
|
||||
case 'ABSTRACT':
|
||||
$this->type = Server::OC_ABSTRACT;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case ABSTRACT returned (%s)',$this->type));
|
||||
break;
|
||||
|
||||
case 'STRUCTURAL':
|
||||
$this->type = Server::OC_STRUCTURAL;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case STRUCTURAL returned (%s)',$this->type));
|
||||
break;
|
||||
|
||||
case 'AUXILIARY':
|
||||
$this->type = Server::OC_AUXILIARY;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case AUXILIARY returned (%s)',$this->type));
|
||||
break;
|
||||
|
||||
case 'MUST':
|
||||
$attrs = collect();
|
||||
|
||||
$i = $this->parseList(++$i,$strings,$attrs);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('= parseList returned %d (%s)',$i,$attrs->join(',')));
|
||||
|
||||
foreach ($attrs as $string) {
|
||||
$attr = new ObjectClassAttribute($string,$this->name);
|
||||
|
||||
if ($server->isForceMay($attr->getName())) {
|
||||
$this->may_force->push($attr);
|
||||
$this->may_attrs->push($attr);
|
||||
|
||||
} else
|
||||
$this->must_attrs->push($attr);
|
||||
}
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case MUST returned (%s) (%s)',$this->must_attrs->join(','),$this->may_force->join(',')));
|
||||
break;
|
||||
|
||||
case 'MAY':
|
||||
$attrs = collect();
|
||||
|
||||
$i = $this->parseList(++$i,$strings,$attrs);
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('parseList returned %d (%s)',$i,$attrs->join(',')));
|
||||
|
||||
foreach ($attrs as $string) {
|
||||
$attr = new ObjectClassAttribute($string,$this->name);
|
||||
$this->may_attrs->push($attr);
|
||||
}
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case MAY returned (%s)',$this->may_attrs->join(',')));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) {
|
||||
$this->oid = $strings[$i];
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('- Case default returned (%s)',$this->oid));
|
||||
|
||||
} elseif ($strings[$i])
|
||||
Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Attributes that this objectclass defines
|
||||
private(set) Collection $attributes;
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
return match ($key) {
|
||||
'attributes' => $this->getAllAttrs(TRUE),
|
||||
'sup' => $this->sup_classes,
|
||||
'all_attributes' => $this->getMustAttrs(TRUE)
|
||||
->merge($this->getMayAttrs(TRUE)),
|
||||
'type_name' => match ($this->type) {
|
||||
Server::OC_STRUCTURAL => 'Structural',
|
||||
Server::OC_ABSTRACT => 'Abstract',
|
||||
@ -220,23 +42,6 @@ final class ObjectClass extends Base
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of attributes that this objectClass provides
|
||||
*
|
||||
* @param bool $parents
|
||||
* @return Collection
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function getAllAttrs(bool $parents=FALSE): Collection
|
||||
{
|
||||
return $this->getMustAttrs($parents)
|
||||
->transform(function($item) {
|
||||
$item->required = true;
|
||||
return $item;
|
||||
})
|
||||
->merge($this->getMayAttrs($parents));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an objectClass to the list of objectClasses that inherit
|
||||
* from this objectClass.
|
||||
@ -245,57 +50,8 @@ final class ObjectClass extends Base
|
||||
*/
|
||||
public function addChildObjectClass(string $name): void
|
||||
{
|
||||
if (! $this->child_objectclasses->contains($name))
|
||||
$this->child_objectclasses->push($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of objectClass names which inherit from this objectClass.
|
||||
*
|
||||
* @return Collection Names of objectClasses which inherit from this objectClass.
|
||||
* @deprecated use $this->child_objectclasses
|
||||
*/
|
||||
public function getChildObjectClasses(): Collection
|
||||
{
|
||||
return $this->child_objectclasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Behaves identically to addMustAttrs, but it operates on the MAY
|
||||
* attributes of this objectClass.
|
||||
*
|
||||
* @param array $attr An array of attribute names (strings) to add.
|
||||
*/
|
||||
private function addMayAttrs(array $attr): void
|
||||
{
|
||||
if (! is_array($attr) || ! count($attr))
|
||||
return;
|
||||
|
||||
$this->may_attrs = $this->may_attrs->merge($attr)->unique();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified array of attributes to this objectClass' list of
|
||||
* MUST attributes. The resulting array of must attributes will contain
|
||||
* unique members.
|
||||
*
|
||||
* @param array $attr An array of attribute names (strings) to add.
|
||||
*/
|
||||
private function addMustAttrs(array $attr): void
|
||||
{
|
||||
if (! is_array($attr) || ! count($attr))
|
||||
return;
|
||||
|
||||
$this->must_attrs = $this->must_attrs->merge($attr)->unique();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
* @deprecated use $this->may_force
|
||||
*/
|
||||
public function getForceMayAttrs(): Collection
|
||||
{
|
||||
return $this->may_force;
|
||||
if (! $this->child_classes->contains($name))
|
||||
$this->child_classes->push($name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -313,42 +69,26 @@ final class ObjectClass extends Base
|
||||
*/
|
||||
public function getMayAttrs(bool $parents=FALSE): Collection
|
||||
{
|
||||
// If we dont need our parents, then we'll just return ours.
|
||||
if (! $parents)
|
||||
return $this->may_attrs
|
||||
->sortBy(fn($item)=>strtolower($item->name.$item->source));
|
||||
$attrs = $this->attributes
|
||||
->filter(fn($item)=>! $item->is_must)
|
||||
->transform(function($item) {
|
||||
$item->source = $this->name;
|
||||
return $item;
|
||||
});
|
||||
|
||||
$attrs = $this->may_attrs;
|
||||
|
||||
foreach ($this->getParents() as $object_class)
|
||||
$attrs = $attrs->merge($object_class->getMayAttrs($parents));
|
||||
|
||||
// Remove any duplicates
|
||||
$attrs = $attrs->unique(function($item) { return $item->name; });
|
||||
if ($parents)
|
||||
foreach ($this->getParents() as $object_class)
|
||||
$attrs = $attrs->merge($object_class
|
||||
->getMayAttrs($parents)
|
||||
->transform(function($item) use ($object_class) {
|
||||
$item->source = $item->source ?: $object_class->name;
|
||||
return $item;
|
||||
}));
|
||||
|
||||
// Return a sorted list
|
||||
return $attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of attribute names (strings) that entries of this ObjectClass must define.
|
||||
* This differs from getMayAttrs in that it returns an array of strings rather than
|
||||
* array of AttributeType objects
|
||||
*
|
||||
* @param bool $parents An array of ObjectClass objects to use when traversing
|
||||
* the inheritance tree. This presents some what of a bootstrapping problem
|
||||
* as we must fetch all objectClasses to determine through inheritance which
|
||||
* attributes this objectClass provides.
|
||||
* @return Collection The array of allowed attribute names (strings).
|
||||
*
|
||||
* @throws InvalidUsage
|
||||
* @see getMustAttrs
|
||||
* @see getMayAttrs
|
||||
* @see getMustAttrNames
|
||||
*/
|
||||
public function getMayAttrNames(bool $parents=FALSE): Collection
|
||||
{
|
||||
return $this->getMayAttrs($parents)->ppluck('name');
|
||||
return $attrs
|
||||
->unique(fn($item)=>$item->name)
|
||||
->sortBy(fn($item)=>$item->name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -365,41 +105,26 @@ final class ObjectClass extends Base
|
||||
*/
|
||||
public function getMustAttrs(bool $parents=FALSE): Collection
|
||||
{
|
||||
// If we dont need our parents, then we'll just return ours.
|
||||
if (! $parents)
|
||||
return $this->must_attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
|
||||
$attrs = $this->attributes
|
||||
->filter(fn($item)=>$item->is_must)
|
||||
->transform(function($item) {
|
||||
$item->source = $this->name;
|
||||
return $item;
|
||||
});
|
||||
|
||||
$attrs = $this->must_attrs;
|
||||
|
||||
foreach ($this->getParents() as $object_class)
|
||||
$attrs = $attrs->merge($object_class->getMustAttrs($parents));
|
||||
|
||||
// Remove any duplicates
|
||||
$attrs = $attrs->unique(function($item) { return $item->name; });
|
||||
if ($parents)
|
||||
foreach ($this->getParents() as $object_class)
|
||||
$attrs = $attrs->merge($object_class
|
||||
->getMustAttrs($parents)
|
||||
->transform(function($item) use ($object_class) {
|
||||
$item->source = $item->source ?: $object_class->name;
|
||||
return $item;
|
||||
}));
|
||||
|
||||
// Return a sorted list
|
||||
return $attrs->sortBy(function($item) { return strtolower($item->name.$item->source); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of attribute names (strings) that entries of this ObjectClass must define.
|
||||
* This differs from getMustAttrs in that it returns an array of strings rather than
|
||||
* array of AttributeType objects
|
||||
*
|
||||
* @param bool $parents An array of ObjectClass objects to use when traversing
|
||||
* the inheritance tree. This presents some what of a bootstrapping problem
|
||||
* as we must fetch all objectClasses to determine through inheritance which
|
||||
* attributes this objectClass provides.
|
||||
* @return Collection The array of allowed attribute names (strings).
|
||||
*
|
||||
* @throws InvalidUsage
|
||||
* @see getMustAttrs
|
||||
* @see getMayAttrs
|
||||
* @see getMayAttrNames
|
||||
*/
|
||||
public function getMustAttrNames(bool $parents=FALSE): Collection
|
||||
{
|
||||
return $this->getMustAttrs($parents)->ppluck('name');
|
||||
return $attrs
|
||||
->unique(fn($item)=>$item->name)
|
||||
->sortBy(fn($item)=>$item->name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -426,27 +151,6 @@ final class ObjectClass extends Base
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the objectClass names from which this objectClass inherits.
|
||||
*
|
||||
* @return Collection An array of objectClass names (strings)
|
||||
* @deprecated use $this->sup_classes;
|
||||
*/
|
||||
public function getSupClasses(): Collection
|
||||
{
|
||||
return $this->sup_classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of this objectClass: STRUCTURAL, ABSTRACT, or AUXILIARY.
|
||||
*
|
||||
* @deprecated use $this->type_name
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if this objectclass is auxiliary
|
||||
*
|
||||
@ -457,39 +161,109 @@ final class ObjectClass extends Base
|
||||
return $this->type === Server::OC_AUXILIARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an array is listed in the may_force attrs
|
||||
*/
|
||||
public function isForceMay(string $attr): bool
|
||||
{
|
||||
return $this->may_force->ppluck('name')->contains($attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if this objectClass is related to $oclass
|
||||
*
|
||||
* @param array $oclass ObjectClasses that this attribute may be related to
|
||||
* @return bool
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function isRelated(array $oclass): bool
|
||||
{
|
||||
// If I am in the array, we'll just return false
|
||||
if (in_array_ignore_case($this->name,$oclass))
|
||||
return FALSE;
|
||||
|
||||
foreach ($oclass as $object_class)
|
||||
if ($object_class->isStructural() && in_array_ignore_case($this->name,$object_class->getParents()->pluck('name')))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
public function isStructural(): bool
|
||||
{
|
||||
return $this->type === Server::OC_STRUCTURAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ObjectClass object given a raw LDAP objectClass string.
|
||||
*
|
||||
* eg: ( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRACT MUST objectClass )
|
||||
*
|
||||
* @param string $line Schema Line
|
||||
*/
|
||||
protected function parse(string $line): void
|
||||
{
|
||||
Log::debug(sprintf('%s:Parsing ObjectClass [%s]',self::LOGKEY,$line));
|
||||
|
||||
// Init
|
||||
$this->attributes = collect();
|
||||
$this->sup_classes = collect();
|
||||
$this->child_classes = collect();
|
||||
|
||||
parent::parse($line);
|
||||
}
|
||||
|
||||
protected function parse_chunk(array $strings,int &$i): void
|
||||
{
|
||||
switch ($strings[$i]) {
|
||||
case 'SUP':
|
||||
if ($strings[$i+1] !== '(') {
|
||||
$this->sup_classes->push(preg_replace("/'/",'',$strings[++$i]));
|
||||
|
||||
} else {
|
||||
$i++;
|
||||
|
||||
do {
|
||||
$i++;
|
||||
|
||||
if ($strings[$i] !== '$')
|
||||
$this->sup_classes->push(preg_replace("/'/",'',$strings[$i]));
|
||||
|
||||
} while (! preg_match('/\)+\)?/',$strings[$i+1]));
|
||||
}
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case SUP returned (%s)',self::LOGKEY,$this->sup_classes->join(',')));
|
||||
break;
|
||||
|
||||
case 'ABSTRACT':
|
||||
$this->type = Server::OC_ABSTRACT;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case ABSTRACT returned (%s)',self::LOGKEY,$this->type));
|
||||
break;
|
||||
|
||||
case 'STRUCTURAL':
|
||||
$this->type = Server::OC_STRUCTURAL;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case STRUCTURAL returned (%s)',self::LOGKEY,$this->type));
|
||||
break;
|
||||
|
||||
case 'AUXILIARY':
|
||||
$this->type = Server::OC_AUXILIARY;
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case AUXILIARY returned (%s)',self::LOGKEY,$this->type));
|
||||
break;
|
||||
|
||||
case 'MUST':
|
||||
$attrs = collect();
|
||||
|
||||
$i = $this->parseList(++$i,$strings,$attrs);
|
||||
|
||||
foreach ($attrs as $string) {
|
||||
$attr = clone config('server')->schema('attributetypes',$string);
|
||||
|
||||
if (! $attr->forced_as_may)
|
||||
$attr->setMust();
|
||||
|
||||
$this->attributes->push($attr);
|
||||
}
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case MUST returned (%s) (%s)',self::LOGKEY,$attrs->join(','),$this->forced_as_may ? 'FORCED MAY' : 'MUST'));
|
||||
break;
|
||||
|
||||
case 'MAY':
|
||||
$attrs = collect();
|
||||
|
||||
$i = $this->parseList(++$i,$strings,$attrs);
|
||||
|
||||
foreach ($attrs as $string)
|
||||
$this->attributes->push(config('server')->schema('attributetypes',$string));
|
||||
|
||||
if (static::DEBUG_VERBOSE)
|
||||
Log::debug(sprintf('%s:- Case MAY returned (%s)',self::LOGKEY,$attrs->join(',')));
|
||||
break;
|
||||
|
||||
default:
|
||||
parent::parse_chunk($strings,$i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an LDAP schema list
|
||||
*
|
||||
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\LDAP\Schema;
|
||||
|
||||
/**
|
||||
* A simple class for representing AttributeTypes used only by the ObjectClass class.
|
||||
*
|
||||
* Users should never instantiate this class. It represents an attribute internal to
|
||||
* an ObjectClass. If PHP supported inner-classes and variable permissions, this would
|
||||
* be interior to class ObjectClass and flagged private. The reason this class is used
|
||||
* and not the "real" class AttributeType is because this class supports the notion of
|
||||
* a "source" objectClass, meaning that it keeps track of which objectClass originally
|
||||
* specified it. This class is therefore used by the class ObjectClass to determine
|
||||
* inheritance.
|
||||
*/
|
||||
final class ObjectClassAttribute extends Base {
|
||||
// This Attribute's root.
|
||||
private string $source;
|
||||
public bool $required = FALSE;
|
||||
|
||||
/**
|
||||
* Creates a new ObjectClassAttribute with specified name and source objectClass.
|
||||
*
|
||||
* @param string $name the name of the new attribute.
|
||||
* @param string $source the name of the ObjectClass which specifies this attribute.
|
||||
*/
|
||||
public function __construct($name,$source)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->source = $source;
|
||||
}
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
return match ($key) {
|
||||
'source' => $this->source,
|
||||
default => parent::__get($key),
|
||||
};
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ use LdapRecord\Query\Builder;
|
||||
use LdapRecord\Query\Collection as LDAPCollection;
|
||||
use LdapRecord\Query\ObjectNotFoundException;
|
||||
|
||||
use App\Classes\LDAP\Schema\{AttributeType,Base,LDAPSyntax,MatchingRule,MatchingRuleUse,ObjectClass};
|
||||
use App\Classes\LDAP\Schema\{AttributeType,Base,LDAPSyntax,MatchingRule,ObjectClass};
|
||||
use App\Exceptions\InvalidUsage;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
@ -28,9 +28,10 @@ final class Server
|
||||
private Collection $attributetypes;
|
||||
private Collection $ldapsyntaxes;
|
||||
private Collection $matchingrules;
|
||||
private Collection $matchingruleuse;
|
||||
private Collection $objectclasses;
|
||||
|
||||
private Model $rootDSE;
|
||||
|
||||
/* ObjectClass Types */
|
||||
public const OC_STRUCTURAL = 0x01;
|
||||
public const OC_ABSTRACT = 0x02;
|
||||
@ -38,6 +39,8 @@ final class Server
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->rootDSE = self::rootDSE();
|
||||
|
||||
$this->attributetypes = collect();
|
||||
$this->ldapsyntaxes = collect();
|
||||
$this->matchingrules = collect();
|
||||
@ -47,10 +50,6 @@ final class Server
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
return match($key) {
|
||||
'attributetypes' => $this->attributetypes,
|
||||
'ldapsyntaxes' => $this->ldapsyntaxes,
|
||||
'matchingrules' => $this->matchingrules,
|
||||
'objectclasses' => $this->objectclasses,
|
||||
'config' => config(sprintf('ldap.connections.%s',config('ldap.default'))),
|
||||
'name' => Arr::get($this->config,'name',__('No Server Name Yet')),
|
||||
default => throw new Exception('Unknown key:'.$key),
|
||||
@ -67,10 +66,10 @@ final class Server
|
||||
* @return Collection
|
||||
* @testedin GetBaseDNTest::testBaseDNExists();
|
||||
*/
|
||||
public static function baseDNs(bool $objects=FALSE): Collection
|
||||
public static function baseDNs(bool $objects=TRUE): Collection
|
||||
{
|
||||
try {
|
||||
$rootdse = self::rootDSE();
|
||||
$namingcontexts = collect(config('pla.base_dns') ?: self::rootDSE()?->namingcontexts);
|
||||
|
||||
/**
|
||||
* LDAP Error Codes:
|
||||
@ -176,16 +175,16 @@ final class Server
|
||||
}
|
||||
|
||||
if (! $objects)
|
||||
return collect($rootdse->namingcontexts);
|
||||
return $namingcontexts;
|
||||
|
||||
return Cache::remember('basedns'.Session::id(),config('ldap.cache.time'),function() use ($rootdse) {
|
||||
return Cache::remember('basedns'.Session::id(),config('ldap.cache.time'),function() use ($namingcontexts) {
|
||||
$result = collect();
|
||||
|
||||
// @note: Incase our rootDSE didnt return a namingcontext, we'll have no base DNs
|
||||
foreach (($rootdse->namingcontexts ?: []) as $dn)
|
||||
foreach ($namingcontexts as $dn)
|
||||
$result->push(self::get($dn)->read()->find($dn));
|
||||
|
||||
return $result->filter();
|
||||
return $result->filter()->sort(fn($item)=>$item->sort_key);
|
||||
});
|
||||
}
|
||||
|
||||
@ -257,18 +256,6 @@ final class Server
|
||||
return $rootdse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Schema DN
|
||||
*
|
||||
* @return string
|
||||
* @throws ObjectNotFoundException
|
||||
*/
|
||||
public static function schemaDN(): string
|
||||
{
|
||||
return collect(self::rootDSE()->subschemasubentry)
|
||||
->first();
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
/**
|
||||
@ -288,7 +275,6 @@ final class Server
|
||||
'c' // Needed for the tree to show icons for countries
|
||||
]))
|
||||
->list()
|
||||
->orderBy('dn')
|
||||
->get() ?: NULL;
|
||||
}
|
||||
|
||||
@ -307,15 +293,37 @@ final class Server
|
||||
}
|
||||
|
||||
/**
|
||||
* This function determines if the specified attribute is contained in the force_may list
|
||||
* as configured in config.php.
|
||||
* Get an attribute key for an attributetype name
|
||||
*
|
||||
* @return boolean True if the specified attribute is configured to be force as a may attribute
|
||||
* @todo There are 3 isForceMay() functions - we only need one
|
||||
* @param string $key
|
||||
* @return int|bool
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function isForceMay($attr_name): bool
|
||||
public function get_attr_id(string $key): int|bool
|
||||
{
|
||||
return in_array($attr_name,config('pla.force_may',[]));
|
||||
static $attributes = $this->schema('attributetypes');
|
||||
|
||||
$attrid = $attributes->search(fn($item)=>$item->names->contains($key));
|
||||
|
||||
// Second chance search using lowercase items (our Entry attribute keys are lowercase)
|
||||
if ($attrid === FALSE)
|
||||
$attrid = $attributes->search(fn($item)=>$item->names_lc->contains(strtolower($key)));
|
||||
|
||||
return $attrid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an OID, return the ldapsyntax for the OID
|
||||
*
|
||||
* @param string $oid
|
||||
* @return LDAPSyntax|null
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function get_syntax(string $oid): ?LDAPSyntax
|
||||
{
|
||||
return (($id=$this->schema('ldapsyntaxes')->search(fn($item)=>$item->oid === $oid)) !== FALSE)
|
||||
? $this->ldapsyntaxes[$id]
|
||||
: NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -336,212 +344,167 @@ final class Server
|
||||
* @param string $item Schema Item to Fetch
|
||||
* @param string|null $key
|
||||
* @return Collection|LDAPSyntax|Base|NULL
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function schema(string $item,?string $key=NULL): Collection|LDAPSyntax|Base|NULL
|
||||
{
|
||||
// Ensure our item to fetch is lower case
|
||||
$item = strtolower($item);
|
||||
if ($key)
|
||||
$key = strtolower($key);
|
||||
|
||||
$result = Cache::remember('schema'.$item,config('ldap.cache.time'),function() use ($item) {
|
||||
// First pass if we have already retrieved the schema item
|
||||
switch ($item) {
|
||||
case 'attributetypes':
|
||||
case 'ldapsyntaxes':
|
||||
case 'matchingrules':
|
||||
case 'objectclasses':
|
||||
if ($this->{$item}->count())
|
||||
return $this->{$item};
|
||||
if (! $this->{$item}->count()) {
|
||||
$this->{$item} = Cache::remember('schema.'.$item,config('ldap.cache.time'),function() use ($item) {
|
||||
// Try to get the schema DN from the specified entry.
|
||||
$schema_dn = $this->schemaDN();
|
||||
// @note: 389DS does not return subschemaSubentry unless it is requested
|
||||
$schema = $this->fetch($schema_dn,['*','+','subschemaSubentry']);
|
||||
|
||||
break;
|
||||
// If our schema's null, we didnt find it.
|
||||
if (! $schema)
|
||||
throw new Exception('Couldnt find schema at:'.$schema_dn);
|
||||
|
||||
// This error message is not localized as only developers should ever see it
|
||||
default:
|
||||
throw new InvalidUsage('Invalid request to fetch schema: '.$item);
|
||||
}
|
||||
switch ($item) {
|
||||
case 'attributetypes':
|
||||
Log::debug(sprintf('%s:Attribute Types',self::LOGKEY));
|
||||
// build the array of attribueTypes
|
||||
//$syntaxes = $this->SchemaSyntaxes($dn);
|
||||
|
||||
// Try to get the schema DN from the specified entry.
|
||||
$schema_dn = $this->schemaDN();
|
||||
// @note: 389DS does not return subschemaSubentry unless it is requested
|
||||
$schema = $this->fetch($schema_dn,['*','+','subschemaSubentry']);
|
||||
|
||||
// If our schema's null, we didnt find it.
|
||||
if (! $schema)
|
||||
throw new Exception('Couldnt find schema at:'.$schema_dn);
|
||||
|
||||
switch ($item) {
|
||||
case 'attributetypes':
|
||||
Log::debug(sprintf('%s:Attribute Types',self::LOGKEY));
|
||||
// build the array of attribueTypes
|
||||
//$syntaxes = $this->SchemaSyntaxes($dn);
|
||||
|
||||
foreach ($schema->{$item} as $line) {
|
||||
if (is_null($line) || ! strlen($line))
|
||||
continue;
|
||||
|
||||
$o = new AttributeType($line);
|
||||
$this->attributetypes->put($o->name_lc,$o);
|
||||
}
|
||||
|
||||
// go back and add data from aliased attributeTypes
|
||||
foreach ($this->attributetypes as $o) {
|
||||
/* foreach of the attribute's aliases, create a new entry in the attrs array
|
||||
* with its name set to the alias name, and all other data copied.*/
|
||||
|
||||
if ($o->aliases->count()) {
|
||||
Log::debug(sprintf('%s:\ Attribute [%s] has the following aliases [%s]',self::LOGKEY,$o->name,$o->aliases->join(',')));
|
||||
|
||||
foreach ($o->aliases as $alias) {
|
||||
$new_attr = clone $o;
|
||||
$new_attr->setName($alias);
|
||||
$new_attr->addAlias($o->name);
|
||||
$new_attr->removeAlias($alias);
|
||||
|
||||
$this->attributetypes->put(strtolower($alias),$new_attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now go through and reference the parent/child relationships
|
||||
foreach ($this->attributetypes as $o)
|
||||
if ($o->sup_attribute) {
|
||||
$parent = strtolower($o->sup_attribute);
|
||||
|
||||
if ($this->attributetypes->has($parent) !== FALSE)
|
||||
$this->attributetypes[$parent]->addChild($o->name);
|
||||
}
|
||||
|
||||
// go through any children and add details if the child doesnt have them (ie, cn inherits name)
|
||||
// @todo This doesnt traverse children properly, so children of children may not get the settings they should
|
||||
foreach ($this->attributetypes as $parent) {
|
||||
foreach ($parent->children as $child) {
|
||||
$child = strtolower($child);
|
||||
|
||||
/* only overwrite the child's SINGLE-VALUE property if the parent has it set, and the child doesnt
|
||||
* (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */
|
||||
if (! is_null($parent->is_single_value) && is_null($this->attributetypes[$child]->is_single_value))
|
||||
$this->attributetypes[$child]->setIsSingleValue($parent->is_single_value);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the used in and required_by values.
|
||||
foreach ($this->schema('objectclasses') as $object_class) {
|
||||
$must_attrs = $object_class->getMustAttrNames();
|
||||
$may_attrs = $object_class->getMayAttrNames();
|
||||
$oclass_attrs = $must_attrs->merge($may_attrs)->unique();
|
||||
|
||||
// Add Used In.
|
||||
foreach ($oclass_attrs as $attr_name)
|
||||
if ($this->attributetypes->has(strtolower($attr_name)))
|
||||
$this->attributetypes[strtolower($attr_name)]->addUsedInObjectClass($object_class->name,$object_class->isStructural());
|
||||
|
||||
// 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,$object_class->isStructural());
|
||||
|
||||
// Force May
|
||||
foreach ($object_class->getForceMayAttrs() as $attr_name)
|
||||
if ($this->attributetypes->has(strtolower($attr_name->name)))
|
||||
$this->attributetypes[strtolower($attr_name->name)]->setForceMay();
|
||||
}
|
||||
|
||||
return $this->attributetypes;
|
||||
|
||||
case 'ldapsyntaxes':
|
||||
Log::debug(sprintf('%s:LDAP Syntaxes',self::LOGKEY));
|
||||
|
||||
foreach ($schema->{$item} as $line) {
|
||||
if (is_null($line) || ! strlen($line))
|
||||
continue;
|
||||
|
||||
$o = new LDAPSyntax($line);
|
||||
$this->ldapsyntaxes->put(strtolower($o->oid),$o);
|
||||
}
|
||||
|
||||
return $this->ldapsyntaxes;
|
||||
|
||||
case 'matchingrules':
|
||||
Log::debug(sprintf('%s:Matching Rules',self::LOGKEY));
|
||||
$this->matchingruleuse = collect();
|
||||
|
||||
foreach ($schema->{$item} as $line) {
|
||||
if (is_null($line) || ! strlen($line))
|
||||
continue;
|
||||
|
||||
$o = new MatchingRule($line);
|
||||
$this->matchingrules->put($o->name_lc,$o);
|
||||
}
|
||||
|
||||
/*
|
||||
* For each MatchingRuleUse entry, add the attributes who use it to the
|
||||
* MatchingRule in the $rules array.
|
||||
*/
|
||||
if ($schema->matchingruleuse) {
|
||||
foreach ($schema->matchingruleuse as $line) {
|
||||
foreach ($schema->{$item} as $line) {
|
||||
if (is_null($line) || ! strlen($line))
|
||||
continue;
|
||||
|
||||
$o = new MatchingRuleUse($line);
|
||||
$this->matchingruleuse->put($o->name_lc,$o);
|
||||
|
||||
if ($this->matchingrules->has($o->name_lc) !== FALSE)
|
||||
$this->matchingrules[$o->name_lc]->setUsedByAttrs($o->getUsedByAttrs());
|
||||
$o = new AttributeType($line);
|
||||
$this->attributetypes->push($o);
|
||||
}
|
||||
|
||||
foreach ($this->attributetypes as $o) {
|
||||
// Now go through and reference the parent/child relationships
|
||||
if ($o->sup_attribute) {
|
||||
$attrid = $this->get_attr_id($o->sup_attribute);
|
||||
|
||||
if (! $this->attributetypes[$attrid]->children->contains($o->oid))
|
||||
$this->attributetypes[$attrid]->addChild($o->oid);
|
||||
}
|
||||
|
||||
// go through any children and add details if the child doesnt have them (ie, cn inherits name)
|
||||
foreach ($o->children as $child) {
|
||||
$attrid = $this->attributetypes->search(fn($o)=>$o->oid === $child);
|
||||
|
||||
/* only overwrite the child's SINGLE-VALUE property if the parent has it set, and the child doesnt
|
||||
* (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */
|
||||
if (! is_null($o->is_single_value) && is_null($this->attributetypes[$attrid]->is_single_value))
|
||||
$this->attributetypes[$attrid]->setIsSingleValue($o->is_single_value);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->attributetypes;
|
||||
|
||||
case 'ldapsyntaxes':
|
||||
Log::debug(sprintf('%s:LDAP Syntaxes',self::LOGKEY));
|
||||
|
||||
foreach ($schema->{$item} as $line) {
|
||||
if (is_null($line) || ! strlen($line))
|
||||
continue;
|
||||
|
||||
$o = new LDAPSyntax($line);
|
||||
$this->ldapsyntaxes->push($o);
|
||||
}
|
||||
|
||||
return $this->ldapsyntaxes;
|
||||
|
||||
case 'matchingrules':
|
||||
Log::debug(sprintf('%s:Matching Rules',self::LOGKEY));
|
||||
|
||||
foreach ($schema->{$item} as $line) {
|
||||
if (is_null($line) || ! strlen($line))
|
||||
continue;
|
||||
|
||||
$o = new MatchingRule($line);
|
||||
$this->matchingrules->push($o);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* No MatchingRuleUse entry in the subschema, so brute-forcing
|
||||
* the reverse-map for the "$rule->getUsedByAttrs()" data.*/
|
||||
foreach ($this->schema('attributetypes') as $attr) {
|
||||
$rule_key = strtolower($attr->getEquality());
|
||||
$rule_id = $this->matchingrules->search(fn($item)=>$item->oid === $attr->equality);
|
||||
|
||||
if ($this->matchingrules->has($rule_key) !== FALSE)
|
||||
$this->matchingrules[$rule_key]->addUsedByAttr($attr->name);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->matchingrules;
|
||||
|
||||
case 'objectclasses':
|
||||
Log::debug(sprintf('%s:Object Classes',self::LOGKEY));
|
||||
|
||||
foreach ($schema->{$item} as $line) {
|
||||
if (is_null($line) || ! strlen($line))
|
||||
continue;
|
||||
|
||||
$o = new ObjectClass($line,$this);
|
||||
$this->objectclasses->put($o->name_lc,$o);
|
||||
}
|
||||
|
||||
// Now go through and reference the parent/child relationships
|
||||
foreach ($this->objectclasses as $o)
|
||||
foreach ($o->getSupClasses() as $parent) {
|
||||
$parent = strtolower($parent);
|
||||
|
||||
if (! $this->objectclasses->contains($parent))
|
||||
$this->objectclasses[$parent]->addChildObjectClass($o->name);
|
||||
if ($rule_id !== FALSE)
|
||||
$this->matchingrules[$rule_id]->addUsedByAttr($attr->name);
|
||||
}
|
||||
|
||||
return $this->objectclasses;
|
||||
return $this->matchingrules;
|
||||
|
||||
// Shouldnt get here
|
||||
default:
|
||||
throw new InvalidUsage('Invalid request to fetch schema: '.$item);
|
||||
}
|
||||
});
|
||||
case 'objectclasses':
|
||||
Log::debug(sprintf('%s:Object Classes',self::LOGKEY));
|
||||
|
||||
return is_null($key) ? $result : $result->get($key);
|
||||
foreach ($schema->{$item} as $line) {
|
||||
if (is_null($line) || ! strlen($line))
|
||||
continue;
|
||||
|
||||
$o = new ObjectClass($line);
|
||||
$this->objectclasses->push($o);
|
||||
}
|
||||
|
||||
foreach ($this->objectclasses as $o) {
|
||||
// Now go through and reference the parent/child relationships
|
||||
foreach ($o->sup_classes as $sup) {
|
||||
$oc_id = $this->objectclasses->search(fn($item)=>$item->name === $sup);
|
||||
|
||||
if (($oc_id !== FALSE) && (! $this->objectclasses[$oc_id]->child_classes->contains($o->name)))
|
||||
$this->objectclasses[$oc_id]->addChildObjectClass($o->name);
|
||||
}
|
||||
|
||||
// Add the used in and required_by values for attributes.
|
||||
foreach ($o->attributes as $attribute) {
|
||||
if (($attrid = $this->schema('attributetypes')->search(fn($item)=>$item->oid === $attribute->oid)) !== FALSE) {
|
||||
// Add Used In.
|
||||
$this->attributetypes[$attrid]->addUsedInObjectClass($o->oid,$o->isStructural());
|
||||
|
||||
// Add Required By.
|
||||
if ($attribute->is_must)
|
||||
$this->attributetypes[$attrid]->addRequiredByObjectClass($o->oid,$o->isStructural());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put the updated attributetypes back in the cache
|
||||
Cache::put('schema.attributetypes',$this->attributetypes,config('ldap.cache.time'));
|
||||
|
||||
return $this->objectclasses;
|
||||
|
||||
// Shouldnt get here
|
||||
default:
|
||||
throw new InvalidUsage('Invalid request to fetch schema: '.$item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (is_null($key))
|
||||
return $this->{$item};
|
||||
|
||||
switch ($item) {
|
||||
case 'attributetypes':
|
||||
$attrid = $this->get_attr_id($key);
|
||||
|
||||
$attr = ($attrid === FALSE)
|
||||
? new AttributeType($key)
|
||||
: clone $this->{$item}->get($attrid);
|
||||
|
||||
$attr->setName($attr->names->get($attr->names_lc->search(strtolower($key))) ?: $key);
|
||||
|
||||
return $attr;
|
||||
|
||||
default:
|
||||
return $this->{$item}->get($key)
|
||||
?: $this->{$item}->first(fn($item)=>$item->name_lc === strtolower($key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an OID, return the ldapsyntax for the OID
|
||||
* Get the Schema DN
|
||||
*
|
||||
* @param string $oid
|
||||
* @return LDAPSyntax|null
|
||||
* @return string
|
||||
* @throws ObjectNotFoundException
|
||||
*/
|
||||
public function schemaSyntaxName(string $oid): ?LDAPSyntax
|
||||
public function schemaDN(): string
|
||||
{
|
||||
return $this->schema('ldapsyntaxes',$oid);
|
||||
return Arr::get($this->rootDSE->subschemasubentry,0);
|
||||
}
|
||||
}
|
450
app/Classes/Template.php
Normal file
450
app/Classes/Template.php
Normal file
@ -0,0 +1,450 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Ldap\Entry;
|
||||
|
||||
class Template
|
||||
{
|
||||
private const LOGKEY = 'T--';
|
||||
private const LOCK_TIME = 600;
|
||||
|
||||
private(set) string $file;
|
||||
private Collection $template;
|
||||
private(set) bool $invalid = FALSE;
|
||||
private(set) string $reason = '';
|
||||
private Collection $on_change_target;
|
||||
private Collection $on_change_attribute;
|
||||
private bool $on_change_processed = FALSE;
|
||||
|
||||
public function __construct(string $file)
|
||||
{
|
||||
$td = Storage::disk(config('pla.template.dir'));
|
||||
$this->on_change_attribute = collect();
|
||||
$this->on_change_target = collect();
|
||||
|
||||
$this->file = $file;
|
||||
|
||||
try {
|
||||
// @todo Load in the proper attribute objects and objectclass objects
|
||||
// @todo Make sure we have a structural objectclass, or make the template invalid
|
||||
$this->template = collect(json_decode($td->get($file),null,512,JSON_OBJECT_AS_ARRAY|JSON_THROW_ON_ERROR));
|
||||
|
||||
} catch (\JsonException $e) {
|
||||
$this->invalid = TRUE;
|
||||
$this->reason = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
return match ($key) {
|
||||
'attributes','objectclasses' => collect($this->template->get($key)),
|
||||
'enabled' => $this->template->get($key,FALSE) && (! $this->invalid),
|
||||
'icon','regexp','title' => $this->template->get($key),
|
||||
'name' => Str::replaceEnd('.json','',$this->file),
|
||||
'order' => $this->attributes->map(fn($item)=>Arr::get($item,'order')),
|
||||
|
||||
default => throw new \Exception('Unknown key: '.$key),
|
||||
};
|
||||
}
|
||||
|
||||
public function __isset(string $key): bool
|
||||
{
|
||||
return $this->template->has($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for an attribute
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return array|NULL
|
||||
*/
|
||||
public function attribute(string $attribute): Collection|NULL
|
||||
{
|
||||
$key = $this->attributes->search(fn($item,$key)=>! strcasecmp($key,$attribute));
|
||||
return collect($this->attributes->get($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an template attributes select options
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return Collection|NULL
|
||||
*/
|
||||
public function attributeOptions(string $attribute): Collection|NULL
|
||||
{
|
||||
return ($x=$this->attribute($attribute)?->get('options'))
|
||||
? collect($x)->map(fn($item,$key)=>['id'=>$key,'value'=>$item])
|
||||
: NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the attribute has been marked as read-only
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return bool
|
||||
*/
|
||||
public function attributeReadOnly(string $attribute): bool
|
||||
{
|
||||
return ($x=$this->attribute($attribute)?->get('readonly')) && $x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the title we should use for an attribute
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return string|NULL
|
||||
*/
|
||||
public function attributeTitle(string $attribute): string|NULL
|
||||
{
|
||||
return $this->attribute($attribute)?->get('display');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the title we should use for an attribute
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return string|NULL
|
||||
*/
|
||||
public function attributeType(string $attribute): string|NULL
|
||||
{
|
||||
return $this->attribute($attribute)?->get('type');
|
||||
}
|
||||
|
||||
public function attributeValue(string $attribute): string|NULL
|
||||
{
|
||||
if ($x=$this->attribute($attribute)->get('value')) {
|
||||
list($command,$args) = preg_split('/^=([a-zA-Z]+)\((.+)\)$/',$x,-1,PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
return match ($command) {
|
||||
'getNextNumber' => $this->getNextNumber($args),
|
||||
default => NULL,
|
||||
};
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next number for an attribute
|
||||
*
|
||||
* As part of getting the next number, we'll use a lock to avoid any potential clashes. The lock is obtained by
|
||||
* two lock files:
|
||||
* a: Read a session lock (our session id), use that number if it exists, otherwise,
|
||||
* b: Query the ldap server for the attribute, sort by number
|
||||
* c: Read a system lock, if it exists, and use that as our start base (otherwise use a config() base)
|
||||
* d: Starting at base, find the next free number
|
||||
* e: When number identified, put it in the system lock with our session id
|
||||
* f: Put the number in our session lock, with a timeout
|
||||
* g: Read the system lock, make sure our session id is still in it, if not, go to (d) with our number as the base
|
||||
* h: Remove our session id from the system lock (our number is unique)
|
||||
*
|
||||
* When using the number to create an entry:
|
||||
* + Read our session lock, confirm the number is still in it, if not fail validation and bounce back
|
||||
* + Create the entry
|
||||
* + Delete our session lock
|
||||
*
|
||||
* @param string $arg
|
||||
* @return int|NULL
|
||||
*/
|
||||
private function getNextNumber(string $arg): int|NULL
|
||||
{
|
||||
if (! preg_match('/;/',$arg)) {
|
||||
Log::alert(sprintf('%s:Invalid argument given to getNextNumber [%s]',self::LOGKEY,$arg));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list($start,$attr) = preg_split('(([^,]+);(\w+))',$arg,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
$attr = strtolower($attr);
|
||||
|
||||
// If we recently got a number, return it
|
||||
if ($number=Cache::get($attr.':'.Session::id()))
|
||||
return $number;
|
||||
|
||||
$cache = Cache::get($attr.':system');
|
||||
Log::debug(sprintf('%s:System Cache has',self::LOGKEY),['cache'=>$cache]);
|
||||
|
||||
if (! Arr::get($cache,'number'))
|
||||
$number = config('pla.template.getnextnumber.'.$attr,0);
|
||||
else
|
||||
$number = Arr::get($cache,'number')+1;
|
||||
|
||||
Log::debug(sprintf('%s:Starting with [%d] for [%s]',self::LOGKEY,$number,$attr));
|
||||
|
||||
$o = config('server');
|
||||
$bases = ($start === '/') ? $o->baseDNs() : collect($start);
|
||||
|
||||
$result = collect();
|
||||
$complete = [];
|
||||
|
||||
do {
|
||||
$sizelimit = FALSE;
|
||||
|
||||
// Get the current numbers
|
||||
foreach ($bases as $base) {
|
||||
if (Arr::get($complete,$dn=$base->getDN()))
|
||||
continue;
|
||||
|
||||
$query = Entry::query()
|
||||
->setDN($base)
|
||||
->select([$attr])
|
||||
->where($attr,'*')
|
||||
->notFilter(fn($q)=>$q->where($attr,'<=',$number-1));
|
||||
|
||||
if ($result->count())
|
||||
$query->notFilter(fn($q)=>$q->where($attr,'>=',$result->min()));
|
||||
|
||||
$result = $result->merge(($x=$query
|
||||
->search()
|
||||
->orderBy($attr)
|
||||
->get())
|
||||
->pluck($attr)
|
||||
->flatten());
|
||||
|
||||
// If we hit a sizelimit on this run
|
||||
$base_sizelimit = $query->getModel()->hasMore();
|
||||
Log::debug(sprintf('%s:Query in [%s] returned [%d] entries and has more [%s]',self::LOGKEY,$base,$x->count(),$base_sizelimit ? 'TRUE' : 'FALSE'));
|
||||
|
||||
if (! $base_sizelimit)
|
||||
$complete[$dn] = TRUE;
|
||||
else
|
||||
Log::info(sprintf('%s:Size Limit alert for [%s]',self::LOGKEY,$dn));
|
||||
|
||||
$sizelimit = $sizelimit || $base_sizelimit;
|
||||
}
|
||||
|
||||
$result = $result
|
||||
->sort()
|
||||
->unique();
|
||||
|
||||
Log::debug(sprintf('%s:Result has [%s]',self::LOGKEY,$result->join('|')));
|
||||
|
||||
if ($result->count())
|
||||
foreach ($result as $item) {
|
||||
Log::debug(sprintf('%s:Checking [%d] against [%s]',self::LOGKEY,$number,$item));
|
||||
if ($number < $item)
|
||||
break;
|
||||
|
||||
$number += 1;
|
||||
}
|
||||
|
||||
else
|
||||
$number += 1;
|
||||
|
||||
// Remove redundant entries
|
||||
$result = $result->filter(fn($item)=>$item>$number);
|
||||
|
||||
if ($sizelimit)
|
||||
Log::debug(sprintf('%s:We got a sizelimit.',self::LOGKEY),['number'=>$number,'result_min'=>$result->min(),'result_count'=>$result->count()]);
|
||||
|
||||
/*
|
||||
* @todo This might need some additional work:
|
||||
* EG: if sizelimit is 5
|
||||
* if result has 1,2,3,4,20 [size limit]
|
||||
* we re-enquire (4=>20) and get 7,8,9,10,11 [size limit]
|
||||
* we re-enquire (4=>7) and get 5,6 [no size limit]
|
||||
* we calculate 12, and accept it because no size limit, but we didnt test for 12
|
||||
*/
|
||||
} while ($sizelimit);
|
||||
|
||||
// We found our number
|
||||
Log::debug(sprintf('%s:Autovalue for Attribute [%s] in Session [%s] Storing [%d]',self::LOGKEY,$attr,Session::id(),$number));
|
||||
Cache::put($attr.':system',['number'=>$number,'session'=>Session::id(),self::LOCK_TIME*2]);
|
||||
Cache::put($attr.':'.Session::id(),$number,self::LOCK_TIME);
|
||||
sleep(1);
|
||||
|
||||
// If the session still has our session ID, then our number is ours
|
||||
return (Arr::get(Cache::get($attr.':system'),'session') === Session::id())
|
||||
? $number
|
||||
: NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the onChange JavaScript for an attribute
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return Collection|NULL
|
||||
*/
|
||||
public function onChange(string $attribute): Collection|NULL
|
||||
{
|
||||
if (! $this->on_change_processed)
|
||||
$this->onChangeProcessing();
|
||||
|
||||
return $this->on_change_attribute
|
||||
->get(strtolower($attribute));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this attribute's value populated by any onChange processing rules
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return bool
|
||||
*/
|
||||
public function onChangeAttribute(string $attribute): bool
|
||||
{
|
||||
if (! $this->on_change_processed)
|
||||
$this->onChangeProcessing();
|
||||
|
||||
return $this->on_change_attribute
|
||||
->has(strtolower($attribute));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the attributes for onChange JavaScript
|
||||
*/
|
||||
/**
|
||||
* Return the onchange JavaScript for attribute
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function onChangeProcessing(): void
|
||||
{
|
||||
foreach (Arr::get($this->template,'attributes',[]) as $attribute => $detail) {
|
||||
$result = collect();
|
||||
|
||||
foreach (Arr::get($detail,'onchange',[]) as $item) {
|
||||
list($command,$args) = preg_split('/^=([a-zA-Z]+)\((.+)\)$/',$item,-1,PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
switch ($command) {
|
||||
case 'autoFill':
|
||||
$result->push($this->autofill($args));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($result->count())
|
||||
$this->on_change_attribute->put(strtolower($attribute),$result);
|
||||
}
|
||||
|
||||
$this->on_change_processed = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this attribute's value populated by any onChange processing rules
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return bool
|
||||
*/
|
||||
public function onChangeTarget(string $attribute): bool
|
||||
{
|
||||
if (! $this->on_change_processed)
|
||||
$this->onChangeProcessing();
|
||||
|
||||
return $this->on_change_target
|
||||
->has(strtolower($attribute));
|
||||
}
|
||||
|
||||
/**
|
||||
* autoFill - javascript to have one attribute fill the value of another
|
||||
*
|
||||
* args: is a literal string, with two parts, delimited by a semi-colon ;
|
||||
* + The first part is the attribute that will be populated
|
||||
* + The second part may contain many fields like %attr|start-end/flags|additionalcontrolchar%
|
||||
* to substitute values read from other fields.
|
||||
* + |start-end is optional, but must be present if the k flag is used
|
||||
* + /flags is optional
|
||||
* + |additionalcontrolchar is optional, and specific to a flag being used
|
||||
*
|
||||
* + flags may be:
|
||||
* T:(?) Read display text from selection item (drop-down list), otherwise, read the value of the field
|
||||
* For fields that aren't selection items, /T shouldn't be used, and the field value will always be read
|
||||
* k:(?) Tokenize:
|
||||
* If the "k" flag is not given:
|
||||
* + A |start-end instruction will perform a sub-string operation upon the value of the attr, passing
|
||||
* character positions start-end through
|
||||
* + start can be 0 for first character, or any other integer
|
||||
* + end can be 0 for last character, or any other integer for a specific position
|
||||
* If the "k" flag is given:
|
||||
* + The string read will be split into fields, using : as a delimiter
|
||||
* + start indicates which field number to pass through
|
||||
*
|
||||
* If additionalcontrolchar is given, it will be used as delimiter (e.g. this allows for splitting
|
||||
* e-mail addresses into domain and domain-local part)
|
||||
* l: Make the result lower case
|
||||
* U: Make the result upper case
|
||||
* A:(?) Remap special characters to their corresponding ASCII value
|
||||
*
|
||||
* @note Attributes rendered on the page are lowercase, eg: <attribute id="gidnumber"> for gidNumber
|
||||
* @note JavaScript generated here depends on js/template.js
|
||||
* (?) = to test
|
||||
*/
|
||||
private function autofill(string $arg): string
|
||||
{
|
||||
if (! preg_match('/;/',$arg)) {
|
||||
Log::alert(sprintf('%s:Invalid argument given to autofill [%s]',self::LOGKEY,$arg));
|
||||
return '';
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
// $attr has our attribute to update, $string is the format to use when updating it
|
||||
list($attr,$string) = preg_split('(([^,]+);(.*))',$arg,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
$this->on_change_target->put(strtolower($attr),$string);
|
||||
|
||||
$output = $string;
|
||||
//$result .= sprintf("\n// %s\n",$arg);
|
||||
|
||||
$m = [];
|
||||
// MATCH : 0 = highlevel match, 1 = attr, 2 = subst, 3 = mod, 4 = delimiter
|
||||
preg_match_all('/%(\w+)(?:\|(\d*-\d)+)?(?:\/([klTUA]+))?(?:\|(.)?)?%/U',$string,$m);
|
||||
|
||||
foreach ($m[0] as $index => $null) {
|
||||
$match_attr = strtolower($m[1][$index]);
|
||||
$match_subst = $m[2][$index];
|
||||
$match_mod = $m[3][$index];
|
||||
$match_delim = $m[4][$index];
|
||||
|
||||
$substrarray = [];
|
||||
|
||||
$result .= sprintf("var %s;\n",$match_attr);
|
||||
|
||||
if (str_contains($match_mod,'k')) {
|
||||
preg_match_all('/(\d+)/',trim($match_subst),$substrarray);
|
||||
|
||||
$delimiter = ($match_delim === '') ? ' ' : preg_quote($match_delim);
|
||||
$result .= sprintf(" %s = %s.split('%s')[%s];\n",$match_attr,$match_attr,$delimiter,$substrarray[1][0] ?? '0');
|
||||
|
||||
} else {
|
||||
// Work out the start and end chars needed from this value if we have a range specifier
|
||||
preg_match_all('/(\d*)-(\d+)/',$match_subst,$substrarray);
|
||||
if ((isset($substrarray[1][0]) && $substrarray[1][0]) || (isset($substrarray[2][0]) && $substrarray[2][0])) {
|
||||
$result .= sprintf("%s = get_attribute('%s',%d,%s);\n",
|
||||
$match_attr,$match_attr,
|
||||
$substrarray[1][0] ?? '0',
|
||||
$substrarray[2][0] ?: sprintf('%s.length',$match_attr));
|
||||
} else {
|
||||
$result .= sprintf("%s = get_attribute('%s');\n",$match_attr,$match_attr);
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($match_mod,'l'))
|
||||
$result .= sprintf("%s = %s.toLowerCase();\n",$match_attr,$match_attr);
|
||||
|
||||
if (str_contains($match_mod,'U'))
|
||||
$result .= sprintf("%s = %s.toUpperCase();\n",$match_attr,$match_attr);
|
||||
|
||||
if (str_contains($match_mod,'A'))
|
||||
$result .= sprintf("%s = toAscii(%s);\n",$match_attr,$match_attr);
|
||||
|
||||
// For debugging
|
||||
//$result .= sprintf("console.log('%s will return:'+%s);\n",$match_attr,$match_attr);
|
||||
|
||||
// Reformat out output into JS variables
|
||||
$output = preg_replace('/'.preg_quote($m[0][$index],'/').'/','\'+'.$match_attr.'+\'',$output);
|
||||
}
|
||||
|
||||
$result .= sprintf("put_attribute('%s','%s');\n",strtolower($attr),$output);
|
||||
$result .= "\n";
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -12,26 +12,24 @@ use App\Classes\LDAP\Server;
|
||||
|
||||
class AjaxController extends Controller
|
||||
{
|
||||
private const LOGKEY = 'CAc';
|
||||
|
||||
/**
|
||||
* Get the LDAP server BASE DNs
|
||||
*
|
||||
* @return Collection
|
||||
* @throws \LdapRecord\Query\ObjectNotFoundException
|
||||
* @todo This should be consolidated with HomeController
|
||||
*/
|
||||
public function bases(): Collection
|
||||
{
|
||||
$base = Server::baseDNs(TRUE) ?: collect();
|
||||
|
||||
return $base
|
||||
->transform(fn($item)=>
|
||||
[
|
||||
'title'=>$item->getRdn(),
|
||||
'item'=>$item->getDNSecure(),
|
||||
'lazy'=>TRUE,
|
||||
'icon'=>'fa-fw fas fa-sitemap',
|
||||
'tooltip'=>$item->getDn(),
|
||||
]);
|
||||
return Server::baseDNs()
|
||||
->map(fn($item)=> [
|
||||
'title'=>$item->getRdn(),
|
||||
'item'=>$item->getDNSecure(),
|
||||
'lazy'=>TRUE,
|
||||
'icon'=>'fa-fw fas fa-sitemap',
|
||||
'tooltip'=>$item->getDn(),
|
||||
])->values();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,13 +38,13 @@ class AjaxController extends Controller
|
||||
*/
|
||||
public function children(Request $request): Collection
|
||||
{
|
||||
$dn = Crypt::decryptString($request->query('key'));
|
||||
$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]',__METHOD__,$dn));
|
||||
Log::debug(sprintf('%s:Query [%s]',self::LOGKEY,$dn));
|
||||
|
||||
return (config('server'))
|
||||
->children($dn)
|
||||
|
@ -7,8 +7,11 @@ use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use LdapRecord\Auth\BindException;
|
||||
use LdapRecord\Container;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Ldap\Entry;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
@ -51,6 +54,45 @@ class LoginController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* When attempt to login
|
||||
*
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
* @throws \LdapRecord\ConnectionException
|
||||
* @throws \LdapRecord\ContainerException
|
||||
*/
|
||||
public function attemptLogin(Request $request)
|
||||
{
|
||||
$attempt = $this->guard()->attempt(
|
||||
$this->credentials($request), $request->boolean('remember')
|
||||
);
|
||||
|
||||
// If the login failed, and PLA is set to use DN login, check if the entry exists.
|
||||
// If the entry doesnt exist, it might be the root DN, which cannot be used to login
|
||||
if ((! $attempt) && $request->dn && config('pla.login.alert_rootdn',TRUE)) {
|
||||
// Double check our credentials, and see if they authenticate
|
||||
try {
|
||||
Container::getInstance()
|
||||
->getConnection()
|
||||
->auth()
|
||||
->bind($request->get(login_attr_name()),$request->get('password'));
|
||||
|
||||
} catch (BindException $e) {
|
||||
// Password incorrect, fail anyway
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$dn = config('server')->fetch($request->dn);
|
||||
$o = new Entry;
|
||||
|
||||
if (! $dn && $o->getConnection()->getLdapConnection()->errNo() === 32)
|
||||
abort(501,'Authentication succeeded, but the DN doesnt exist');
|
||||
}
|
||||
|
||||
return $attempt;
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to delete our encrypted username/password cookies
|
||||
*
|
||||
|
@ -6,17 +6,18 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use LdapRecord\Exceptions\InsufficientAccessException;
|
||||
use LdapRecord\LdapRecordException;
|
||||
use LdapRecord\Query\ObjectNotFoundException;
|
||||
use Nette\NotImplementedException;
|
||||
|
||||
use App\Classes\LDAP\Attribute\{Factory,Password};
|
||||
use App\Classes\LDAP\Server;
|
||||
use App\Classes\LDAP\Import\LDIF as LDIFImport;
|
||||
use App\Classes\LDAP\Export\LDIF as LDIFExport;
|
||||
use App\Exceptions\Import\{GeneralException,VersionException};
|
||||
@ -26,20 +27,9 @@ use App\Ldap\Entry;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
private function bases(): Collection
|
||||
{
|
||||
$base = Server::baseDNs(TRUE) ?: collect();
|
||||
private const LOGKEY = 'CHc';
|
||||
|
||||
return $base->transform(function($item) {
|
||||
return [
|
||||
'title'=>$item->getRdn(),
|
||||
'item'=>$item->getDNSecure(),
|
||||
'lazy'=>TRUE,
|
||||
'icon'=>'fa-fw fas fa-sitemap',
|
||||
'tooltip'=>$item->getDn(),
|
||||
];
|
||||
});
|
||||
}
|
||||
private const INTERNAL_POST = ['_auto_value','_key','_rdn','_rdn_value','_step','_template','_token','_userpassword_hash'];
|
||||
|
||||
/**
|
||||
* Create a new object in the LDAP server
|
||||
@ -50,30 +40,45 @@ class HomeController extends Controller
|
||||
*/
|
||||
public function entry_add(EntryAddRequest $request): \Illuminate\View\View
|
||||
{
|
||||
if (! old('step',$request->validated('step')))
|
||||
if (! old('_step',$request->validated('_step')))
|
||||
abort(404);
|
||||
|
||||
$key = $this->request_key($request,collect(old()));
|
||||
|
||||
$template = NULL;
|
||||
$o = new Entry;
|
||||
$o->setRDNBase($key['dn']);
|
||||
|
||||
if (count($x=array_filter(old('objectclass',$request->objectclass)))) {
|
||||
$o->objectclass = $x;
|
||||
foreach (collect(old())->except(self::INTERNAL_POST) as $old => $value)
|
||||
$o->{$old} = array_filter($value);
|
||||
|
||||
if (old('_template',$request->validated('template'))) {
|
||||
$template = $o->template(old('_template',$request->validated('template')));
|
||||
|
||||
$o->objectclass = [Entry::TAG_NOTAG=>$template->objectclasses->toArray()];
|
||||
|
||||
foreach ($o->getAvailableAttributes()
|
||||
->filter(fn($item)=>$item->names_lc->intersect($template->attributes->keys()->map('strtolower'))->count())
|
||||
->sortBy(fn($item)=>Arr::get($template->order,$item->name)) as $ao)
|
||||
{
|
||||
$o->{$ao->name} = [Entry::TAG_NOTAG=>''];
|
||||
}
|
||||
|
||||
} elseif (count($x=collect(old('objectclass',$request->validated('objectclass')))->dot()->filter())) {
|
||||
$o->objectclass = Arr::undot($x);
|
||||
|
||||
// Also add in our required attributes
|
||||
foreach($o->getAvailableAttributes()->filter(fn($item)=>$item->required) as $ao)
|
||||
foreach ($o->getAvailableAttributes()->filter(fn($item)=>$item->is_must) as $ao)
|
||||
$o->{$ao->name} = [Entry::TAG_NOTAG=>''];
|
||||
|
||||
$o->setRDNBase($key['dn']);
|
||||
}
|
||||
|
||||
$step = $request->step ? $request->step+1 : old('step');
|
||||
$step = $request->get('_step') ? $request->get('_step')+1 : old('_step');
|
||||
|
||||
return view('frame')
|
||||
->with('subframe','create')
|
||||
->with('bases',$this->bases())
|
||||
->with('o',$o)
|
||||
->with('step',$step)
|
||||
->with('template',$template)
|
||||
->with('container',old('container',$key['dn']));
|
||||
}
|
||||
|
||||
@ -103,6 +108,7 @@ class HomeController extends Controller
|
||||
return $view
|
||||
->with('o',$o)
|
||||
->with('langtag',Entry::TAG_NOTAG)
|
||||
->with('template',NULL)
|
||||
->with('updated',FALSE);
|
||||
}
|
||||
|
||||
@ -110,14 +116,21 @@ class HomeController extends Controller
|
||||
{
|
||||
$key = $this->request_key($request,collect(old()));
|
||||
|
||||
$dn = sprintf('%s=%s,%s',$request->rdn,$request->rdn_value,$key['dn']);
|
||||
$dn = sprintf('%s=%s,%s',$request->get('_rdn'),$request->get('_rdn_value'),$key['dn']);
|
||||
|
||||
$o = new Entry;
|
||||
$o->setDn($dn);
|
||||
|
||||
foreach ($request->except(['_token','key','step','rdn','rdn_value','userpassword_hash']) as $key => $value)
|
||||
foreach ($request->except(self::INTERNAL_POST) as $key => $value)
|
||||
$o->{$key} = array_filter($value);
|
||||
|
||||
// We need to process and encrypt the password
|
||||
if ($request->userpassword)
|
||||
$o->userpassword = $this->password(
|
||||
$o->getObject('userpassword'),
|
||||
$request->userpassword,
|
||||
$request->get('_userpassword_hash'));
|
||||
|
||||
try {
|
||||
$o->save();
|
||||
|
||||
@ -146,6 +159,12 @@ class HomeController extends Controller
|
||||
));
|
||||
}
|
||||
|
||||
// If there are an _auto_value attributes, we need to invalid those
|
||||
foreach ($request->get('_auto_value',[]) as $attr => $value) {
|
||||
Log::debug(sprintf('%s:Removing auto_value attr [%s]',self::LOGKEY,$attr));
|
||||
Cache::delete($attr.':'.Session::id());
|
||||
}
|
||||
|
||||
return Redirect::to('/')
|
||||
->withFragment($o->getDNSecure());
|
||||
}
|
||||
@ -211,7 +230,7 @@ class HomeController extends Controller
|
||||
*/
|
||||
public function entry_objectclass_add(Request $request): Collection
|
||||
{
|
||||
$dn = $request->key ? Crypt::decryptString($request->dn) : '';
|
||||
$dn = $request->get('_key') ? Crypt::decryptString($request->dn) : '';
|
||||
$oc = Factory::create($dn,'objectclass',$request->oc);
|
||||
|
||||
$ocs = $oc
|
||||
@ -266,31 +285,17 @@ class HomeController extends Controller
|
||||
|
||||
$o = config('server')->fetch($dn);
|
||||
|
||||
foreach ($request->except(['_token','dn','userpassword_hash','userpassword']) as $key => $value)
|
||||
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 = [];
|
||||
$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($po,$dotkey)) && ($value === md5($old))) {
|
||||
$passwords[$dotkey] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value) {
|
||||
$type = Arr::get($request->userpassword_hash,$dotkey);
|
||||
$passwords[$dotkey] = Password::hash_id($type)
|
||||
->encode($value);
|
||||
}
|
||||
}
|
||||
|
||||
$o->userpassword = Arr::undot($passwords);
|
||||
}
|
||||
if ($request->userpassword)
|
||||
$o->userpassword = $this->password(
|
||||
$o->getObject('userpassword'),
|
||||
$request->userpassword,
|
||||
$request->get('_userpassword_hash'));
|
||||
|
||||
if (! $o->getDirty())
|
||||
return back()
|
||||
@ -298,7 +303,6 @@ class HomeController extends Controller
|
||||
->with('note',__('No attributes changed'));
|
||||
|
||||
return view('update')
|
||||
->with('bases',$this->bases())
|
||||
->with('dn',$dn)
|
||||
->with('o',$o);
|
||||
}
|
||||
@ -368,6 +372,7 @@ class HomeController extends Controller
|
||||
* @param Request $request
|
||||
* @param Collection|null $old
|
||||
* @return \Illuminate\View\View
|
||||
* @throws InvalidUsage
|
||||
*/
|
||||
public function frame(Request $request,?Collection $old=NULL): \Illuminate\View\View
|
||||
{
|
||||
@ -377,22 +382,28 @@ class HomeController extends Controller
|
||||
|
||||
$key = $this->request_key($request,$old);
|
||||
|
||||
$view = ($old
|
||||
$view = $old
|
||||
? view('frame')->with('subframe',$key['cmd'])
|
||||
: view('frames.'.$key['cmd']))
|
||||
->with('bases',$this->bases());
|
||||
: view('frames.'.$key['cmd']);
|
||||
|
||||
// If we are rendering a DN, rebuild our object
|
||||
if ($key['dn']) {
|
||||
if ($key['cmd'] === 'create') {
|
||||
$o = new Entry;
|
||||
$o->setRDNBase($key['dn']);
|
||||
|
||||
} elseif ($key['dn']) {
|
||||
// @todo Need to handle if DN is null, for example if the user's session expired and the ACLs dont let them retrieve $key['dn']
|
||||
$o = config('server')->fetch($key['dn']);
|
||||
|
||||
foreach (collect(old())->except(['key','dn','step','_token','userpassword_hash','rdn','rdn_value']) as $attr => $value)
|
||||
foreach (collect(old())->except(array_merge(self::INTERNAL_POST,['dn'])) as $attr => $value)
|
||||
$o->{$attr} = $value;
|
||||
}
|
||||
|
||||
return match ($key['cmd']) {
|
||||
'create' => $view
|
||||
->with('container',old('container',$key['dn']))
|
||||
->with('o',$o)
|
||||
->with('template',NULL)
|
||||
->with('step',1),
|
||||
|
||||
'dn' => $view
|
||||
@ -420,8 +431,7 @@ class HomeController extends Controller
|
||||
// Did we come here as a result of a redirect
|
||||
return count(old())
|
||||
? $this->frame($request,collect(old()))
|
||||
: view('home')
|
||||
->with('bases',$this->bases());
|
||||
: view('home');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -457,11 +467,32 @@ class HomeController extends Controller
|
||||
|
||||
return view('frame')
|
||||
->with('subframe','import_result')
|
||||
->with('bases',$this->bases())
|
||||
->with('result',$result)
|
||||
->with('ldif',htmlspecialchars($x));
|
||||
}
|
||||
|
||||
private function password(Password $po,array $values,array $hash): array
|
||||
{
|
||||
// We need to process and encrypt the password
|
||||
$passwords = [];
|
||||
|
||||
foreach (Arr::dot($values) as $dotkey => $value) {
|
||||
// If the password is still the MD5 of the old password, then it hasnt changed
|
||||
if (($old=Arr::get($po,$dotkey)) && ($value === md5($old))) {
|
||||
$passwords[$dotkey] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value) {
|
||||
$type = Arr::get($hash,$dotkey);
|
||||
$passwords[$dotkey] = Password::hash_id($type)
|
||||
->encode($value);
|
||||
}
|
||||
}
|
||||
|
||||
return Arr::undot($passwords);
|
||||
}
|
||||
|
||||
/**
|
||||
* For any incoming request, work out the command and DN involved
|
||||
*
|
||||
@ -474,8 +505,8 @@ class HomeController extends Controller
|
||||
// Setup
|
||||
$cmd = NULL;
|
||||
$dn = NULL;
|
||||
$key = $request->get('key',old('key'))
|
||||
? Crypt::decryptString($request->get('key',old('key')))
|
||||
$key = $request->get('_key',old('_key'))
|
||||
? Crypt::decryptString($request->get('_key',old('_key')))
|
||||
: NULL;
|
||||
|
||||
// Determine if our key has a command
|
||||
@ -487,9 +518,9 @@ class HomeController extends Controller
|
||||
$dn = ($m[2] !== '_NOP') ? $m[2] : NULL;
|
||||
}
|
||||
|
||||
} elseif (old('dn',$request->get('key'))) {
|
||||
} elseif (old('dn',$request->get('_key'))) {
|
||||
$cmd = 'dn';
|
||||
$dn = Crypt::decryptString(old('dn',$request->get('key')));
|
||||
$dn = Crypt::decryptString(old('dn',$request->get('_key')));
|
||||
}
|
||||
|
||||
return ['cmd'=>$cmd,'dn'=>$dn];
|
||||
@ -506,12 +537,12 @@ class HomeController extends Controller
|
||||
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)))
|
||||
if ($request->type && $request->get('_key') && (! config('server')->schema($request->type)->has($request->get('_key'))))
|
||||
abort(404);
|
||||
|
||||
return view('frames.schema')
|
||||
->with('type',$request->type)
|
||||
->with('key',$request->key);
|
||||
->with('key',$request->get('_key'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,7 @@ class SearchController extends Controller
|
||||
|
||||
$result = collect();
|
||||
|
||||
foreach ($so->baseDNs() as $base) {
|
||||
foreach ($so->baseDNs(FALSE) as $base) {
|
||||
$search = (new Entry)
|
||||
->in($base);
|
||||
|
||||
|
46
app/Http/Middleware/AcceptLanguage.php
Normal file
46
app/Http/Middleware/AcceptLanguage.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AcceptLanguage
|
||||
{
|
||||
private const LOGKEY = 'MAL';
|
||||
|
||||
public function handle(Request $request,Closure $next): mixed
|
||||
{
|
||||
if ($locale=$this->parseHttpLocale($request)) {
|
||||
Log::debug(sprintf('%s:Accept Language changed from [%s] to [%s] from Browser (%s)',self::LOGKEY,app()->getLocale(),$locale,$request->header('Accept-Language')));
|
||||
|
||||
app()->setLocale($locale);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
private function parseHttpLocale(Request $request): string
|
||||
{
|
||||
$list = explode(',',$request->server('HTTP_ACCEPT_LANGUAGE',''));
|
||||
|
||||
$locales = Collection::make($list)
|
||||
->map(function ($locale) {
|
||||
$parts = explode(';',$locale);
|
||||
$mapping = [];
|
||||
|
||||
$mapping['locale'] = trim($parts[0]);
|
||||
$mapping['factor'] = isset($parts[1])
|
||||
? Arr::get(explode('=',$parts[1]),1)
|
||||
: 1;
|
||||
|
||||
return $mapping;
|
||||
})
|
||||
->sortByDesc(fn($locale)=>$locale['factor']);
|
||||
|
||||
return Arr::get($locales->first(),'locale');
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
use App\Classes\LDAP\Server;
|
||||
|
||||
/**
|
||||
* This sets up our application session with any required values, ultimately for cache optimisation reasons
|
||||
*/
|
||||
class ApplicationSession
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request,Closure $next): mixed
|
||||
{
|
||||
Config::set('server',new Server);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Classes\LDAP\Server;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
@ -29,7 +30,7 @@ class SwapinAuthUser
|
||||
if (! array_key_exists($key,config('ldap.connections')))
|
||||
abort(599,sprintf('LDAP default server [%s] configuration doesnt exist?',$key));
|
||||
|
||||
if (Session::has('username_encrypt') && Session::has('password_encrypt')) {
|
||||
if (($request->path() !== 'logout') && Session::has('username_encrypt') && Session::has('password_encrypt')) {
|
||||
Config::set('ldap.connections.'.$key.'.username',Crypt::decryptString(Session::get('username_encrypt')));
|
||||
Config::set('ldap.connections.'.$key.'.password',Crypt::decryptString(Session::get('password_encrypt')));
|
||||
|
||||
@ -43,6 +44,8 @@ class SwapinAuthUser
|
||||
$c->setConfiguration(config('ldap.connections.'.$key));
|
||||
$c->setGuardResolver(fn()=>new Guard($c->getLdapConnection(),$c->getConfiguration()));
|
||||
|
||||
Config::set('server',new Server);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@ -3,12 +3,17 @@
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
use App\Rules\{DNExists,HasStructuralObjectClass};
|
||||
|
||||
class EntryAddRequest extends FormRequest
|
||||
{
|
||||
private const LOGKEY = 'EAR';
|
||||
|
||||
/**
|
||||
* Get the error messages for the defined validation rules.
|
||||
*
|
||||
@ -17,8 +22,8 @@ class EntryAddRequest extends FormRequest
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'rdn' => __('RDN is required.'),
|
||||
'rdn_value' => __('RDN value is required.'),
|
||||
'_rdn' => __('RDN is required.'),
|
||||
'_rdn_value' => __('RDN value is required.'),
|
||||
];
|
||||
}
|
||||
|
||||
@ -35,14 +40,23 @@ class EntryAddRequest extends FormRequest
|
||||
return [];
|
||||
|
||||
$r = request() ?: collect();
|
||||
$rk = array_keys($r->all());
|
||||
|
||||
return config('server')
|
||||
->schema('attributetypes')
|
||||
->intersectByKeys($r->all())
|
||||
->filter(fn($item)=>$item->names_lc->intersect($rk)->count())
|
||||
->transform(function($item) use ($rk) {
|
||||
// Set the attributetype name
|
||||
if (($x=$item->names_lc->intersect($rk))->count() === 1)
|
||||
$item->setName($x->pop());
|
||||
|
||||
return $item;
|
||||
})
|
||||
->map(fn($item)=>$item->validation($r->get('objectclass',[])))
|
||||
->filter()
|
||||
->flatMap(fn($item)=>$item)
|
||||
->merge([
|
||||
'key' => [
|
||||
'_key' => [
|
||||
'required',
|
||||
new DNExists,
|
||||
function (string $attribute,mixed $value,\Closure $fail) {
|
||||
@ -57,20 +71,68 @@ class EntryAddRequest extends FormRequest
|
||||
}
|
||||
},
|
||||
],
|
||||
'rdn' => 'required_if:step,2|string|min:1',
|
||||
'rdn_value' => 'required_if:step,2|string|min:1',
|
||||
'step' => 'int|min:1|max:2',
|
||||
'_rdn' => 'required_if:_step,2|string|min:1',
|
||||
'_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',
|
||||
'objectclass._null_' => [
|
||||
function (string $attribute,mixed $value,\Closure $fail) {
|
||||
$oc = collect($value)->dot()->filter();
|
||||
|
||||
// If this is step 1 and there is no objectclass, and no template, then fail
|
||||
if ((! $oc->count())
|
||||
&& (request()->post('_step') == 1)
|
||||
&& (! request()->post('template')))
|
||||
{
|
||||
$fail(__('Select an objectclass or a template'));
|
||||
}
|
||||
|
||||
// Cant have both an objectclass and a template
|
||||
if (request()->post('template') && $oc->count())
|
||||
$fail(__('You cannot select a template and an objectclass'));
|
||||
},
|
||||
'array',
|
||||
'min:1',
|
||||
new HasStructuralObjectClass,
|
||||
],
|
||||
'template' => [
|
||||
function (string $attribute,mixed $value,\Closure $fail) {
|
||||
$oc = collect(request()->post('objectclass'))->dot()->filter();
|
||||
|
||||
// If this is step 1 and there is no objectclass, and no template, then fail
|
||||
if ((! collect($value)->filter()->count())
|
||||
&& (request()->post('_step') == 1)
|
||||
&& (! $oc->count()))
|
||||
{
|
||||
$fail(__('Select an objectclass or a template'));
|
||||
}
|
||||
|
||||
// Cant have both an objectclass and a template
|
||||
if ($oc->count() && strlen($value))
|
||||
$fail(__('You cannot select a template and an objectclass'));
|
||||
},
|
||||
],
|
||||
'_auto_value' => 'nullable|array|min:1',
|
||||
'_auto_value.*' => [
|
||||
'nullable',
|
||||
function (string $attribute,mixed $value,\Closure $fail) {
|
||||
$attr = preg_replace('/^_auto_value\./','',$attribute);
|
||||
|
||||
// If the value has been overritten, then our auto_value is invalid
|
||||
if (! collect(request()->get($attr))->dot()->contains($value))
|
||||
return;
|
||||
|
||||
$cache = Cache::get($attr.':'.Session::id());
|
||||
Log::debug(sprintf('%s:Autovalue for Attribute [%s] in Session [%s] Retrieved [%d](%d)',self::LOGKEY,$attr,Session::id(),$cache,$value));
|
||||
|
||||
if ($cache !== (int)$value)
|
||||
$fail(__('Lock expired, please re-submit.'));
|
||||
}
|
||||
]
|
||||
])
|
||||
->toArray();
|
||||
|
@ -10,14 +10,24 @@ class EntryRequest extends FormRequest
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
* @throws \Psr\Container\ContainerExceptionInterface
|
||||
* @throws \Psr\Container\NotFoundExceptionInterface
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$r = request() ?: collect();
|
||||
$rk = array_keys($r->all());
|
||||
|
||||
return config('server')
|
||||
->schema('attributetypes')
|
||||
->intersectByKeys($r->all())
|
||||
->filter(fn($item)=>$item->names_lc->intersect($rk)->count())
|
||||
->transform(function($item) use ($rk) {
|
||||
// Set the attributetype name
|
||||
if (($x=$item->names_lc->intersect($rk))->count() === 1)
|
||||
$item->setName($x->pop());
|
||||
|
||||
return $item;
|
||||
})
|
||||
->map(fn($item)=>$item->validation($r->get('objectclass',[])))
|
||||
->filter()
|
||||
->flatMap(fn($item)=>$item)
|
||||
|
@ -3,10 +3,16 @@
|
||||
namespace App\Ldap;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use LdapRecord\Support\Arr;
|
||||
use LdapRecord\Models\Model;
|
||||
|
||||
use App\Classes\Template;
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Classes\LDAP\Attribute\Factory;
|
||||
use App\Classes\LDAP\Export\LDIF;
|
||||
@ -38,10 +44,31 @@ class Entry extends Model
|
||||
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
$this->objects = collect();
|
||||
$this->templates = collect(['default'=>__('LDAP Entry')]);
|
||||
|
||||
parent::__construct($attributes);
|
||||
|
||||
$this->objects = collect();
|
||||
|
||||
// Load any templates
|
||||
$this->templates = Cache::remember('templates'.Session::id(),config('ldap.cache.time'),function() {
|
||||
$template_dir = Storage::disk(config('pla.template.dir'));
|
||||
$templates = collect();
|
||||
|
||||
foreach (array_filter($template_dir->files('.',TRUE),fn($item)=>Str::endsWith($item,'.json')) as $file) {
|
||||
if (config('pla.template.exclude_system',FALSE) && Str::doesntContain($file,'/'))
|
||||
continue;
|
||||
|
||||
$to = new Template($file);
|
||||
|
||||
if ($to->invalid) {
|
||||
Log::debug(sprintf('Template [%s] is not valid (%s) - ignoring',$file,$to->reason));
|
||||
|
||||
} else {
|
||||
$templates->put($file,new Template($file));
|
||||
}
|
||||
}
|
||||
|
||||
return $templates;
|
||||
});
|
||||
}
|
||||
|
||||
public function discardChanges(): static
|
||||
@ -131,6 +158,17 @@ class Entry extends Model
|
||||
$this->objects = collect();
|
||||
}
|
||||
|
||||
// Filter out our templates specific for this entry
|
||||
if ($this->dn && (! in_array(strtolower($this->dn),['cn=subschema']))) {
|
||||
$this->templates = $this->templates
|
||||
->filter(fn($item)=>$item->enabled
|
||||
&& (! $item->objectclasses
|
||||
->map('strtolower')
|
||||
->diff(array_map('strtolower',Arr::get($this->attributes,'objectclass')))
|
||||
->count()))
|
||||
->sortBy(fn($item)=>$item->title);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -170,13 +208,13 @@ class Entry extends Model
|
||||
// If the attribute name has tags
|
||||
list($attribute,$tag) = $this->keytag($key);
|
||||
|
||||
if (! config('server')->schema('attributetypes')->has($attribute))
|
||||
if (config('server')->get_attr_id($attribute) === FALSE)
|
||||
throw new AttributeException(sprintf('Schema doesnt have attribute [%s]',$attribute));
|
||||
|
||||
$o = $this->objects->get($attribute) ?: Attribute\Factory::create($this->dn ?: '',$attribute,[]);
|
||||
$o->addValue($tag,[$value]);
|
||||
|
||||
$this->objects->put($attribute,$o);
|
||||
$this->objects->put($key,$o);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,7 +318,7 @@ class Entry extends Model
|
||||
$result = collect();
|
||||
|
||||
foreach (($this->getObject('objectclass')?->values ?: []) as $oc)
|
||||
$result = $result->merge(config('server')->schema('objectclasses',$oc)->attributes);
|
||||
$result = $result->merge(config('server')->schema('objectclasses',$oc)->all_attributes);
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -383,9 +421,6 @@ class Entry extends Model
|
||||
* 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
|
||||
{
|
||||
@ -398,7 +433,7 @@ class Entry extends Model
|
||||
$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));
|
||||
$o->setAttributes($this->getAvailableAttributes()->filter(fn($item)=>$item->is_must));
|
||||
|
||||
return $o;
|
||||
}
|
||||
@ -430,6 +465,21 @@ class Entry extends Model
|
||||
->has($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Did this query generate a size limit exception
|
||||
*
|
||||
* @return bool
|
||||
* @throws \LdapRecord\ContainerException
|
||||
*/
|
||||
public function hasMore(): bool
|
||||
{
|
||||
return $this->getConnectionContainer()
|
||||
->getConnection()
|
||||
->getLdapConnection()
|
||||
->getDetailedError()
|
||||
?->getErrorCode() === 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an icon for a DN based on objectClass
|
||||
*
|
||||
@ -530,5 +580,13 @@ class Entry extends Model
|
||||
throw new InvalidUsage('Cannot set RDN base on existing entries');
|
||||
|
||||
$this->rdnbase = $bdn;
|
||||
|
||||
$this->templates = $this->templates
|
||||
->filter(fn($item)=>(! $item->regexp) || preg_match($item->regexp,$bdn));
|
||||
}
|
||||
|
||||
public function template(string $item): Template|Null
|
||||
{
|
||||
return Arr::get($this->templates,$item);
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use LdapRecord\Configuration\DomainConfiguration;
|
||||
use LdapRecord\Laravel\LdapRecord;
|
||||
@ -29,11 +28,5 @@ class AppServiceProvider extends ServiceProvider
|
||||
public function boot(): void
|
||||
{
|
||||
$this->loadViewsFrom(__DIR__.'/../../resources/themes/architect/views/','architect');
|
||||
|
||||
// Enable pluck on collections to work on private values
|
||||
Collection::macro('ppluck',
|
||||
fn($attr)=>$this
|
||||
->map(fn($item)=>$item->{$attr})
|
||||
->values());
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ class HasStructuralObjectClass implements ValidationRule
|
||||
if ($item && config('server')->schema('objectclasses',$item)->isStructural())
|
||||
return;
|
||||
|
||||
$fail('There isnt a Structural Objectclass.');
|
||||
if (collect($value)->dot()->filter()->count())
|
||||
$fail(__('There isnt a Structural Objectclass.'));
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
use App\Classes\LDAP\Attribute as LDAPAttribute;
|
||||
use App\Classes\Template;
|
||||
|
||||
class Attribute extends Component
|
||||
{
|
||||
@ -14,17 +15,19 @@ class Attribute extends Component
|
||||
public bool $new;
|
||||
public bool $old;
|
||||
public bool $updated;
|
||||
public ?Template $template;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(?LDAPAttribute $o,bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE)
|
||||
public function __construct(?LDAPAttribute $o,bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,bool $updated=FALSE,?Template $template=NULL)
|
||||
{
|
||||
$this->o = $o;
|
||||
$this->edit = $edit;
|
||||
$this->old = $old;
|
||||
$this->new = $new;
|
||||
$this->updated = $updated;
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +39,12 @@ class Attribute extends Component
|
||||
{
|
||||
return $this->o
|
||||
? $this->o
|
||||
->render(edit: $this->edit,old: $this->old,new: $this->new,updated: $this->updated)
|
||||
->render(
|
||||
edit: $this->edit,
|
||||
old: $this->old,
|
||||
new: $this->new,
|
||||
updated: $this->updated,
|
||||
template: $this->template)
|
||||
: __('Unknown');
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
|
||||
use App\Http\Middleware\{AllowAnonymous,ApplicationSession,CheckUpdate,SwapinAuthUser,ViewVariables};
|
||||
use App\Http\Middleware\{AcceptLanguage,AllowAnonymous,CheckUpdate,SwapinAuthUser,ViewVariables};
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
@ -16,7 +16,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
$middleware->appendToGroup(
|
||||
group: 'web',
|
||||
middleware: [
|
||||
ApplicationSession::class,
|
||||
AcceptLanguage::class,
|
||||
AllowAnonymous::class,
|
||||
SwapinAuthUser::class,
|
||||
ViewVariables::class,
|
||||
|
@ -15,6 +15,7 @@
|
||||
"laravel/ui": "^4.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"amirami/localizator": "^0.14@dev",
|
||||
"barryvdh/laravel-debugbar": "^3.6",
|
||||
"fakerphp/faker": "^1.23",
|
||||
"mockery/mockery": "^1.6",
|
||||
|
251
composer.lock
generated
251
composer.lock
generated
@ -4,20 +4,20 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "fe524438312a0b5d565ad83c191eac6a",
|
||||
"content-hash": "baf49824fa0f2a2a61b7915204b18d2c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.12.3",
|
||||
"version": "0.13.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/brick/math.git",
|
||||
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
|
||||
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
|
||||
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04",
|
||||
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -56,7 +56,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/brick/math/issues",
|
||||
"source": "https://github.com/brick/math/tree/0.12.3"
|
||||
"source": "https://github.com/brick/math/tree/0.13.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -64,7 +64,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-28T13:11:00+00:00"
|
||||
"time": "2025-03-29T13:50:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "carbonphp/carbon-doctrine-types",
|
||||
@ -288,16 +288,16 @@
|
||||
},
|
||||
{
|
||||
"name": "directorytree/ldaprecord-laravel",
|
||||
"version": "v3.4.1",
|
||||
"version": "v3.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DirectoryTree/LdapRecord-Laravel.git",
|
||||
"reference": "15f56e01319852d41023633d3688ac4aa139aa6e"
|
||||
"reference": "28c5a7aa42aa3fa631f9c0f0c8236fd19bc7b00c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/DirectoryTree/LdapRecord-Laravel/zipball/15f56e01319852d41023633d3688ac4aa139aa6e",
|
||||
"reference": "15f56e01319852d41023633d3688ac4aa139aa6e",
|
||||
"url": "https://api.github.com/repos/DirectoryTree/LdapRecord-Laravel/zipball/28c5a7aa42aa3fa631f9c0f0c8236fd19bc7b00c",
|
||||
"reference": "28c5a7aa42aa3fa631f9c0f0c8236fd19bc7b00c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -343,7 +343,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/DirectoryTree/LdapRecord-Laravel/issues",
|
||||
"source": "https://github.com/DirectoryTree/LdapRecord-Laravel/tree/v3.4.1"
|
||||
"source": "https://github.com/DirectoryTree/LdapRecord-Laravel/tree/v3.4.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -351,7 +351,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-21T19:16:44+00:00"
|
||||
"time": "2025-06-13T15:46:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/inflector",
|
||||
@ -1199,20 +1199,20 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.16.0",
|
||||
"version": "v12.19.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "293bb1c70224faebfd3d4328e201c37115da055f"
|
||||
"reference": "4e6ec689ef704bb4bd282f29d9dd658dfb4fb262"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/293bb1c70224faebfd3d4328e201c37115da055f",
|
||||
"reference": "293bb1c70224faebfd3d4328e201c37115da055f",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/4e6ec689ef704bb4bd282f29d9dd658dfb4fb262",
|
||||
"reference": "4e6ec689ef704bb4bd282f29d9dd658dfb4fb262",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.11|^0.12",
|
||||
"brick/math": "^0.11|^0.12|^0.13",
|
||||
"composer-runtime-api": "^2.2",
|
||||
"doctrine/inflector": "^2.0.5",
|
||||
"dragonmantank/cron-expression": "^3.4",
|
||||
@ -1410,7 +1410,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2025-05-27T15:49:44+00:00"
|
||||
"time": "2025-06-18T12:56:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
@ -2315,16 +2315,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "3.9.1",
|
||||
"version": "3.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/CarbonPHP/carbon.git",
|
||||
"reference": "ced71f79398ece168e24f7f7710462f462310d4d"
|
||||
"reference": "c1397390dd0a7e0f11660f0ae20f753d88c1f3d9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ced71f79398ece168e24f7f7710462f462310d4d",
|
||||
"reference": "ced71f79398ece168e24f7f7710462f462310d4d",
|
||||
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/c1397390dd0a7e0f11660f0ae20f753d88c1f3d9",
|
||||
"reference": "c1397390dd0a7e0f11660f0ae20f753d88c1f3d9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2332,9 +2332,9 @@
|
||||
"ext-json": "*",
|
||||
"php": "^8.1",
|
||||
"psr/clock": "^1.0",
|
||||
"symfony/clock": "^6.3 || ^7.0",
|
||||
"symfony/clock": "^6.3.12 || ^7.0",
|
||||
"symfony/polyfill-mbstring": "^1.0",
|
||||
"symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0"
|
||||
"symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/clock-implementation": "1.0"
|
||||
@ -2342,14 +2342,13 @@
|
||||
"require-dev": {
|
||||
"doctrine/dbal": "^3.6.3 || ^4.0",
|
||||
"doctrine/orm": "^2.15.2 || ^3.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.57.2",
|
||||
"friendsofphp/php-cs-fixer": "^3.75.0",
|
||||
"kylekatarnls/multi-tester": "^2.5.3",
|
||||
"ondrejmirtes/better-reflection": "^6.25.0.4",
|
||||
"phpmd/phpmd": "^2.15.0",
|
||||
"phpstan/extension-installer": "^1.3.1",
|
||||
"phpstan/phpstan": "^1.11.2",
|
||||
"phpunit/phpunit": "^10.5.20",
|
||||
"squizlabs/php_codesniffer": "^3.9.0"
|
||||
"phpstan/extension-installer": "^1.4.3",
|
||||
"phpstan/phpstan": "^2.1.17",
|
||||
"phpunit/phpunit": "^10.5.46",
|
||||
"squizlabs/php_codesniffer": "^3.13.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/carbon"
|
||||
@ -2417,7 +2416,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-01T19:51:51+00:00"
|
||||
"time": "2025-06-12T10:24:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/schema",
|
||||
@ -2483,16 +2482,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v4.0.6",
|
||||
"version": "v4.0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "ce708655043c7050eb050df361c5e313cf708309"
|
||||
"reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/ce708655043c7050eb050df361c5e313cf708309",
|
||||
"reference": "ce708655043c7050eb050df361c5e313cf708309",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2",
|
||||
"reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2563,9 +2562,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v4.0.6"
|
||||
"source": "https://github.com/nette/utils/tree/v4.0.7"
|
||||
},
|
||||
"time": "2025-03-30T21:06:30+00:00"
|
||||
"time": "2025-06-03T04:55:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nunomaduro/termwind",
|
||||
@ -3263,16 +3262,16 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "4.8.0",
|
||||
"version": "4.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "6700833915c00f890615fbcb653faed513836836"
|
||||
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/6700833915c00f890615fbcb653faed513836836",
|
||||
"reference": "6700833915c00f890615fbcb653faed513836836",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
|
||||
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3336,9 +3335,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/uuid/issues",
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.8.0"
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.8.1"
|
||||
},
|
||||
"time": "2025-06-01T02:32:15+00:00"
|
||||
"time": "2025-06-01T06:28:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
@ -5848,6 +5847,84 @@
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "amirami/localizator",
|
||||
"version": "v0.14.0-alpha",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/amiranagram/localizator.git",
|
||||
"reference": "30aae5e51d3aaa95105e3834d481b8c37efaab40"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/amiranagram/localizator/zipball/30aae5e51d3aaa95105e3834d481b8c37efaab40",
|
||||
"reference": "30aae5e51d3aaa95105e3834d481b8c37efaab40",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/config": "^8.0|^9.0|^10.0|^11.0|^12.0",
|
||||
"illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0",
|
||||
"illuminate/filesystem": "^8.0|^9.0|^10.0|^11.0|^12.0",
|
||||
"illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0",
|
||||
"php": "^7.2.5|^8.0",
|
||||
"symfony/finder": "^5.1|^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench": "^6.6|^7.0|^8.0|^9.0|^10.0",
|
||||
"phpunit/phpunit": "^9.5|^10.0|^11.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Amirami\\Localizator\\ServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Amirami\\Localizator\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Amir Rami",
|
||||
"email": "me@amirrami.com",
|
||||
"homepage": "https://amirrami.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Localizator is a small tool for Laravel that gives you the ability to extract untranslated strings from project files. It works using the artisan command line and the provided localize command.",
|
||||
"homepage": "https://github.com/amiranagram/localizator",
|
||||
"support": {
|
||||
"issues": "https://github.com/amiranagram/localizator/issues",
|
||||
"source": "https://github.com/amiranagram/localizator/tree/v0.14.0-alpha"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://m.do.co/c/f38828dd20f8",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://www.buymeacoffee.com/amirami/",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/amiranagram",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-06T12:49:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
"version": "v3.15.4",
|
||||
@ -5998,16 +6075,16 @@
|
||||
},
|
||||
{
|
||||
"name": "filp/whoops",
|
||||
"version": "2.18.0",
|
||||
"version": "2.18.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filp/whoops.git",
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e"
|
||||
"reference": "59a123a3d459c5a23055802237cb317f609867e5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5",
|
||||
"reference": "59a123a3d459c5a23055802237cb317f609867e5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -6057,7 +6134,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/filp/whoops/issues",
|
||||
"source": "https://github.com/filp/whoops/tree/2.18.0"
|
||||
"source": "https://github.com/filp/whoops/tree/2.18.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -6065,7 +6142,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-15T12:00:00+00:00"
|
||||
"time": "2025-06-16T00:02:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "hamcrest/hamcrest-php",
|
||||
@ -6321,23 +6398,23 @@
|
||||
},
|
||||
{
|
||||
"name": "nunomaduro/collision",
|
||||
"version": "v8.8.0",
|
||||
"version": "v8.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nunomaduro/collision.git",
|
||||
"reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8"
|
||||
"reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/4cf9f3b47afff38b139fb79ce54fc71799022ce8",
|
||||
"reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8",
|
||||
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
|
||||
"reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"filp/whoops": "^2.18.0",
|
||||
"nunomaduro/termwind": "^2.3.0",
|
||||
"filp/whoops": "^2.18.1",
|
||||
"nunomaduro/termwind": "^2.3.1",
|
||||
"php": "^8.2.0",
|
||||
"symfony/console": "^7.2.5"
|
||||
"symfony/console": "^7.3.0"
|
||||
},
|
||||
"conflict": {
|
||||
"laravel/framework": "<11.44.2 || >=13.0.0",
|
||||
@ -6345,15 +6422,15 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "^7.8.3",
|
||||
"larastan/larastan": "^3.2",
|
||||
"laravel/framework": "^11.44.2 || ^12.6",
|
||||
"laravel/pint": "^1.21.2",
|
||||
"laravel/sail": "^1.41.0",
|
||||
"laravel/sanctum": "^4.0.8",
|
||||
"larastan/larastan": "^3.4.2",
|
||||
"laravel/framework": "^11.44.2 || ^12.18",
|
||||
"laravel/pint": "^1.22.1",
|
||||
"laravel/sail": "^1.43.1",
|
||||
"laravel/sanctum": "^4.1.1",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"orchestra/testbench-core": "^9.12.0 || ^10.1",
|
||||
"pestphp/pest": "^3.8.0",
|
||||
"sebastian/environment": "^7.2.0 || ^8.0"
|
||||
"orchestra/testbench-core": "^9.12.0 || ^10.4",
|
||||
"pestphp/pest": "^3.8.2",
|
||||
"sebastian/environment": "^7.2.1 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@ -6416,7 +6493,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-03T14:33:09+00:00"
|
||||
"time": "2025-06-11T01:04:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -6608,16 +6685,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "11.0.9",
|
||||
"version": "11.0.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7"
|
||||
"reference": "1a800a7446add2d79cc6b3c01c45381810367d76"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/14d63fbcca18457e49c6f8bebaa91a87e8e188d7",
|
||||
"reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1a800a7446add2d79cc6b3c01c45381810367d76",
|
||||
"reference": "1a800a7446add2d79cc6b3c01c45381810367d76",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -6674,15 +6751,27 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.9"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/show"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sebastianbergmann",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://liberapay.com/sebastianbergmann",
|
||||
"type": "liberapay"
|
||||
},
|
||||
{
|
||||
"url": "https://thanks.dev/u/gh/sebastianbergmann",
|
||||
"type": "thanks_dev"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-25T13:26:39+00:00"
|
||||
"time": "2025-06-18T08:56:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -6931,16 +7020,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "11.5.21",
|
||||
"version": "11.5.24",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289"
|
||||
"reference": "6b07ab1047155cf38f82dd691787a277782271dd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d565e2cdc21a7db9dc6c399c1fc2083b8010f289",
|
||||
"reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6b07ab1047155cf38f82dd691787a277782271dd",
|
||||
"reference": "6b07ab1047155cf38f82dd691787a277782271dd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -6954,7 +7043,7 @@
|
||||
"phar-io/manifest": "^2.0.4",
|
||||
"phar-io/version": "^3.2.1",
|
||||
"php": ">=8.2",
|
||||
"phpunit/php-code-coverage": "^11.0.9",
|
||||
"phpunit/php-code-coverage": "^11.0.10",
|
||||
"phpunit/php-file-iterator": "^5.1.0",
|
||||
"phpunit/php-invoker": "^5.0.1",
|
||||
"phpunit/php-text-template": "^4.0.1",
|
||||
@ -7012,7 +7101,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.21"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.24"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -7036,7 +7125,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-21T12:35:00+00:00"
|
||||
"time": "2025-06-20T11:31:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@ -8461,7 +8550,9 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {},
|
||||
"stability-flags": {
|
||||
"amirami/localizator": 20
|
||||
},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
@ -43,19 +43,6 @@ return [
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| the application so that it's available within Artisan commands.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|
10
config/filesystems.php
Normal file
10
config/filesystems.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'disks' => [
|
||||
'templates' => [
|
||||
'driver' => 'local',
|
||||
'root' => base_path(env('LDAP_TEMPLATE_DIR','templates')),
|
||||
],
|
||||
],
|
||||
];
|
@ -35,7 +35,6 @@ return [
|
||||
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
||||
'password' => env('LDAP_PASSWORD', 'secret'),
|
||||
'port' => env('LDAP_PORT', 389),
|
||||
'base_dn' => env('LDAP_BASE_DN', 'dc=local,dc=com'),
|
||||
'timeout' => env('LDAP_TIMEOUT', 5),
|
||||
'use_ssl' => env('LDAP_SSL', false),
|
||||
'use_tls' => env('LDAP_TLS', false),
|
||||
@ -47,7 +46,6 @@ return [
|
||||
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
||||
'password' => env('LDAP_PASSWORD', 'secret'),
|
||||
'port' => env('LDAP_PORT', 636),
|
||||
'base_dn' => env('LDAP_BASE_DN', 'dc=local,dc=com'),
|
||||
'timeout' => env('LDAP_TIMEOUT', 5),
|
||||
'use_ssl' => env('LDAP_SSL', true),
|
||||
'use_tls' => env('LDAP_TLS', false),
|
||||
@ -59,7 +57,6 @@ return [
|
||||
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
||||
'password' => env('LDAP_PASSWORD', 'secret'),
|
||||
'port' => env('LDAP_PORT', 389),
|
||||
'base_dn' => env('LDAP_BASE_DN', 'dc=local,dc=com'),
|
||||
'timeout' => env('LDAP_TIMEOUT', 5),
|
||||
'use_ssl' => env('LDAP_SSL', false),
|
||||
'use_tls' => env('LDAP_TLS', true),
|
||||
@ -72,7 +69,6 @@ return [
|
||||
'username' => 'cn=Directory Manager',
|
||||
'password' => 'password',
|
||||
'port' => 1389,
|
||||
'base_dn' => 'dc=example,dc=com',
|
||||
'timeout' => env('LDAP_TIMEOUT', 5),
|
||||
'use_ssl' => env('LDAP_SSL', false),
|
||||
'use_tls' => env('LDAP_TLS', false),
|
||||
|
59
config/localizator.php
Normal file
59
config/localizator.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/**
|
||||
* Localize types of translation strings.
|
||||
*/
|
||||
'localize' => [
|
||||
/**
|
||||
* Short keys. This is the default for Laravel.
|
||||
* They are stored in PHP files inside folders name by their locale code.
|
||||
* Laravel comes with default: auth.php, pagination.php, passwords.php and validation.php
|
||||
*/
|
||||
'default' => true,
|
||||
|
||||
/**
|
||||
* Translations strings as key.
|
||||
* They are stored in JSON file for each locale.
|
||||
*/
|
||||
'json' => true,
|
||||
],
|
||||
|
||||
/**
|
||||
* Search criteria for files.
|
||||
*/
|
||||
'search' => [
|
||||
/**
|
||||
* Directories which should be looked inside.
|
||||
*/
|
||||
'dirs' => ['app','resources/views'],
|
||||
|
||||
/**
|
||||
* Subdirectories which will be excluded.
|
||||
* The values must be relative to the included directory paths.
|
||||
*/
|
||||
'exclude' => [
|
||||
//
|
||||
],
|
||||
|
||||
/**
|
||||
* Patterns by which files should be queried.
|
||||
* The values can be a regular expression, glob, or just a string.
|
||||
*/
|
||||
'patterns' => ['*.php'],
|
||||
|
||||
/**
|
||||
* Functions that the strings will be extracted from.
|
||||
* Add here any custom defined functions.
|
||||
* NOTE: The translation string should always be the first argument.
|
||||
*/
|
||||
'functions' => ['__', 'trans', '@lang']
|
||||
],
|
||||
|
||||
/**
|
||||
* Should the localize command sort extracted strings alphabetically?
|
||||
*/
|
||||
'sort' => true,
|
||||
|
||||
];
|
@ -43,6 +43,17 @@ return [
|
||||
|
||||
'allow_guest' => env('LDAP_ALLOW_GUEST',FALSE),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Base DNs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Normally PLA will get the base DNs from the rootDSE's namingcontexts
|
||||
| entry. Instead of using that, you can define your own base DNs to use.
|
||||
|
|
||||
*/
|
||||
'base_dns' => ($x=env('LDAP_BASE_DN', NULL)) ? explode(':',$x) : NULL,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Date Format
|
||||
@ -73,7 +84,20 @@ return [
|
||||
* setup.
|
||||
*/
|
||||
'login' => [
|
||||
'attr' => [env('LDAP_LOGIN_ATTR','uid') => env('LDAP_LOGIN_ATTR_DESC','User ID')], // Attribute used to find user for login
|
||||
'objectclass' => explode(',',env('LDAP_LOGIN_OBJECTCLASS', 'posixAccount')), // Objectclass that users must contain to login
|
||||
// Attribute used to find user for login
|
||||
'attr' => [strtolower(env('LDAP_LOGIN_ATTR','uid')) => env('LDAP_LOGIN_ATTR_DESC','User ID')],
|
||||
// Objectclass that users must contain to login
|
||||
'objectclass' => explode(',',env('LDAP_LOGIN_OBJECTCLASS', 'posixAccount')),
|
||||
// Alert if DN is being used, and the login fails, and the the DN doesnt exist
|
||||
'alert_rootdn' => env('LDAP_ALERT_ROOTDN',TRUE) && strtolower(env('LDAP_LOGIN_ATTR','uid')) === 'dn',
|
||||
],
|
||||
|
||||
'template' => [
|
||||
'dir' => env('LDAP_TEMPLATE_DRIVER','templates'),
|
||||
'exclude_system' => env('LDAP_TEMPLATE_EXCLUDE_SYSTEM',FALSE),
|
||||
'getnextnumber' => [
|
||||
'gidnumber' => env('LDAP_TEMPLATE_GIDNUMBER_START', 1000),
|
||||
'uidnumber' => env('LDAP_TEMPLATE_UIDNUMBER_START', 1000),
|
||||
],
|
||||
],
|
||||
];
|
358
package-lock.json
generated
358
package-lock.json
generated
@ -53,9 +53,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.3.tgz",
|
||||
"integrity": "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw==",
|
||||
"version": "7.27.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz",
|
||||
"integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@ -101,12 +101,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz",
|
||||
"integrity": "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==",
|
||||
"version": "7.27.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz",
|
||||
"integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.27.3",
|
||||
"@babel/parser": "^7.27.5",
|
||||
"@babel/types": "^7.27.3",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
@ -378,22 +378,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.4.tgz",
|
||||
"integrity": "sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ==",
|
||||
"version": "7.27.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz",
|
||||
"integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.27.3"
|
||||
"@babel/types": "^7.27.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz",
|
||||
"integrity": "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==",
|
||||
"version": "7.27.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz",
|
||||
"integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.27.3"
|
||||
@ -651,9 +651,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-block-scoping": {
|
||||
"version": "7.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.3.tgz",
|
||||
"integrity": "sha512-+F8CnfhuLhwUACIJMLWnjz6zvzYM2r0yeIHKlbgfw7ml8rOMJsXNXV/hyRcb3nb493gRs4WvYpQAndWj/qQmkQ==",
|
||||
"version": "7.27.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz",
|
||||
"integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
@ -1189,9 +1189,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-regenerator": {
|
||||
"version": "7.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.4.tgz",
|
||||
"integrity": "sha512-Glp/0n8xuj+E1588otw5rjJkTXfzW7FjH3IIUrfqiZOPQCd2vbg8e+DQE8jK9g4V5/zrxFW+D9WM9gboRPELpQ==",
|
||||
"version": "7.27.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.5.tgz",
|
||||
"integrity": "sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.27.1"
|
||||
@ -1509,9 +1509,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.4.tgz",
|
||||
"integrity": "sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA==",
|
||||
"version": "7.27.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
|
||||
"integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@ -1550,9 +1550,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz",
|
||||
"integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==",
|
||||
"version": "7.27.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz",
|
||||
"integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
@ -2046,9 +2046,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
|
||||
"version": "1.19.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
|
||||
"integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/connect": "*",
|
||||
@ -2114,15 +2114,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz",
|
||||
"integrity": "sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==",
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz",
|
||||
"integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
@ -2166,9 +2166,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-errors": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
|
||||
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
||||
"integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/http-proxy": {
|
||||
@ -2245,12 +2245,12 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.15.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz",
|
||||
"integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==",
|
||||
"version": "24.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz",
|
||||
"integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
"undici-types": "~7.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node-forge": {
|
||||
@ -2287,9 +2287,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
||||
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
|
||||
"version": "0.17.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
|
||||
"integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
@ -2306,9 +2306,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "1.15.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
|
||||
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
|
||||
"version": "1.15.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
|
||||
"integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
@ -2557,9 +2557,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@ -2800,10 +2800,25 @@
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"possible-typed-array-names": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
|
||||
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
|
||||
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
@ -3020,9 +3035,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/bootstrap": {
|
||||
"version": "5.3.6",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
|
||||
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
|
||||
"version": "5.3.7",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz",
|
||||
"integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -3055,9 +3070,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@ -3312,9 +3327,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001720",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz",
|
||||
"integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==",
|
||||
"version": "1.0.30001724",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz",
|
||||
"integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -3674,12 +3689,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.42.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz",
|
||||
"integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==",
|
||||
"version": "3.43.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz",
|
||||
"integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.24.4"
|
||||
"browserslist": "^4.25.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@ -4325,9 +4340,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.161",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.161.tgz",
|
||||
"integrity": "sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA==",
|
||||
"version": "1.5.171",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz",
|
||||
"integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/elliptic": {
|
||||
@ -4908,15 +4923,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/for-each": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-callable": "^1.2.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
||||
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
|
||||
"integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
@ -5631,9 +5662,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz",
|
||||
"integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==",
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
|
||||
"integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
@ -5730,6 +5761,18 @@
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-callable": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
@ -5835,6 +5878,21 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-typed-array": {
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
|
||||
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"which-typed-array": "^1.1.16"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-what": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
|
||||
@ -7050,21 +7108,53 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pbkdf2": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
|
||||
"integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz",
|
||||
"integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"create-hash": "^1.1.2",
|
||||
"create-hmac": "^1.1.4",
|
||||
"ripemd160": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1",
|
||||
"sha.js": "^2.4.8"
|
||||
"create-hash": "~1.1.3",
|
||||
"create-hmac": "^1.1.7",
|
||||
"ripemd160": "=2.0.1",
|
||||
"safe-buffer": "^5.2.1",
|
||||
"sha.js": "^2.4.11",
|
||||
"to-buffer": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/pbkdf2/node_modules/create-hash": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
|
||||
"integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"inherits": "^2.0.1",
|
||||
"ripemd160": "^2.0.0",
|
||||
"sha.js": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pbkdf2/node_modules/hash-base": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
|
||||
"integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pbkdf2/node_modules/ripemd160": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz",
|
||||
"integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hash-base": "^2.0.0",
|
||||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pe7-icon": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pe7-icon/-/pe7-icon-1.0.4.tgz",
|
||||
@ -7116,10 +7206,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/possible-typed-array-names": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
||||
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz",
|
||||
"integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==",
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -8222,9 +8321,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.89.1",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.89.1.tgz",
|
||||
"integrity": "sha512-eMLLkl+qz7tx/0cJ9wI+w09GQ2zodTkcE/aVfywwdlRcI3EO19xGnbmJwg/JMIm+5MxVJ6outddLZ4Von4E++Q==",
|
||||
"version": "1.89.2",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz",
|
||||
"integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
@ -8604,9 +8703,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/shell-quote": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
|
||||
"integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
|
||||
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@ -8995,9 +9094,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.40.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.40.0.tgz",
|
||||
"integrity": "sha512-cfeKl/jjwSR5ar7d0FGmave9hFGJT8obyo0z+CrQOylLDbk7X81nPU6vq9VORa5jU30SkDnT2FXjLbR8HLP+xA==",
|
||||
"version": "5.43.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
||||
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
@ -9129,6 +9228,26 @@
|
||||
"integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-buffer": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz",
|
||||
"integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"isarray": "^2.0.5",
|
||||
"safe-buffer": "^5.2.1",
|
||||
"typed-array-buffer": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/to-buffer/node_modules/isarray": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@ -9175,10 +9294,24 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typed-array-buffer": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
|
||||
"integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.3",
|
||||
"es-errors": "^1.3.0",
|
||||
"is-typed-array": "^1.1.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unicode-canonical-property-names-ecmascript": {
|
||||
@ -9790,9 +9923,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/webpack-sources": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.0.tgz",
|
||||
"integrity": "sha512-77R0RDmJfj9dyv5p3bM5pOHa+X8/ZkO9c7kpDstigkC4nIDobadsfSGCwB4bKhMVxqAok8tajaoR8rirM7+VFQ==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz",
|
||||
"integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
@ -9854,6 +9987,27 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/which-typed-array": {
|
||||
"version": "1.1.19",
|
||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
|
||||
"integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"available-typed-arrays": "^1.0.7",
|
||||
"call-bind": "^1.0.8",
|
||||
"call-bound": "^1.0.4",
|
||||
"for-each": "^0.3.5",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-tostringtag": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/wildcard": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
|
||||
|
@ -14,11 +14,20 @@ attribute#objectclass .input-group-end:not(input.form-control) {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.input-group:first-child .select2-container--bootstrap-5 .select2-selection {
|
||||
/* select forms that have nothing next to them */
|
||||
.select-group:first-child .select2-container--bootstrap-5 .select2-selection {
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.input-group:first-child:not(.select-group) .select2-container--bootstrap-5 .select2-selection {
|
||||
border-bottom-right-radius: unset;
|
||||
border-top-right-radius: unset;
|
||||
}
|
||||
|
||||
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||
font-size: 0.88em;
|
||||
}
|
||||
|
||||
input.form-control.input-group-end {
|
||||
border-bottom-right-radius: 4px !important;
|
||||
border-top-right-radius: 4px !important;
|
||||
|
@ -263,3 +263,9 @@ select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__
|
||||
body.modal-open {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Fix our search results, implementing a scroll bar */
|
||||
#search_results ul.typeahead.dropdown-menu {
|
||||
overflow-y: scroll;
|
||||
max-height: 300px;
|
||||
}
|
@ -15,7 +15,7 @@ function getNode(item) {
|
||||
$.ajax({
|
||||
url: '/frame',
|
||||
method: 'POST',
|
||||
data: { key: item },
|
||||
data: { _key: item },
|
||||
dataType: 'html',
|
||||
beforeSend: function() {
|
||||
content = $('.main-content')
|
||||
@ -37,11 +37,11 @@ function getNode(item) {
|
||||
$('.main-content').empty().append(e.responseText);
|
||||
break;
|
||||
case 409: // Not in root
|
||||
location.replace('/#'+item);
|
||||
break;
|
||||
case 419: // Session Expired
|
||||
location.replace('/#'+item);
|
||||
location.reload();
|
||||
// When the session expires, and we are in the tree, we need to force a reload
|
||||
if (location.pathname === '/')
|
||||
location.reload();
|
||||
break;
|
||||
case 500:
|
||||
case 555: // Missing Method
|
||||
@ -59,7 +59,7 @@ $(document).ready(function() {
|
||||
if (typeof basedn !== 'undefined') {
|
||||
sources = basedn;
|
||||
} else {
|
||||
sources = { url: 'ajax/bases' };
|
||||
sources = { url: '/ajax/bases' };
|
||||
}
|
||||
|
||||
// Attach the fancytree widget to an existing <div id="tree"> element
|
||||
@ -96,7 +96,7 @@ $(document).ready(function() {
|
||||
lazyLoad: function(event,data) {
|
||||
data.result = {
|
||||
url: '/ajax/children',
|
||||
data: {key: data.node.data.item,depth: 1}
|
||||
data: {_key: data.node.data.item,depth: 1}
|
||||
};
|
||||
|
||||
expandChildren(data.tree.rootNode);
|
||||
|
23
public/js/template.js
Normal file
23
public/js/template.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* JavaScript template engine abstraction layer */
|
||||
/* Currently implemented for jquery */
|
||||
|
||||
// Get a value from an attribute
|
||||
function get_attribute(attribute,start,end) {
|
||||
var val = $('#'+attribute).find('input').val();
|
||||
|
||||
return ((start !== undefined) && (end !== undefined))
|
||||
? val.substring(start,end)
|
||||
: val;
|
||||
}
|
||||
|
||||
// Put a value to an attribute
|
||||
function put_attribute(attribute,result) {
|
||||
// Get the value, if the value hasnt changed, then we dont need to do anything
|
||||
if (get_attribute(attribute) === result)
|
||||
return;
|
||||
|
||||
$('#'+attribute)
|
||||
.find('input')
|
||||
.val(result)
|
||||
.trigger('change');
|
||||
}
|
81
public/js/toAscii.js
Normal file
81
public/js/toAscii.js
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// Purpose of this file is to remap characters as ASCII characters
|
||||
//
|
||||
//
|
||||
|
||||
var to_ascii_array = new Array();
|
||||
to_ascii_array['Ã '] = 'a';
|
||||
to_ascii_array['á'] = 'a';
|
||||
to_ascii_array['â'] = 'a';
|
||||
to_ascii_array['À'] = 'a';
|
||||
to_ascii_array['ã'] = 'a';
|
||||
to_ascii_array['Ã¥'] = 'a';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã'] = 'A';
|
||||
to_ascii_array['Ã
'] = 'A';
|
||||
to_ascii_array['é'] = 'e';
|
||||
to_ascii_array['Ú'] = 'e';
|
||||
to_ascii_array['ë'] = 'e';
|
||||
to_ascii_array['ê'] = 'e';
|
||||
to_ascii_array['â¬'] = 'E';
|
||||
to_ascii_array['ï'] = 'i';
|
||||
to_ascii_array['î'] = 'i';
|
||||
to_ascii_array['ì'] = 'i';
|
||||
to_ascii_array['Ã'] = 'i';
|
||||
to_ascii_array['Ã'] = 'I';
|
||||
to_ascii_array['Ã'] = 'I';
|
||||
to_ascii_array['Ã'] = 'I';
|
||||
to_ascii_array['Ã'] = 'I';
|
||||
to_ascii_array['ò'] = 'o';
|
||||
to_ascii_array['ó'] = 'o';
|
||||
to_ascii_array['ÃŽ'] = 'o';
|
||||
to_ascii_array['õ'] = 'o';
|
||||
to_ascii_array['ö'] = 'o';
|
||||
to_ascii_array['Þ'] = 'o';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['Ã'] = 'O';
|
||||
to_ascii_array['ù'] = 'u';
|
||||
to_ascii_array['ú'] = 'u';
|
||||
to_ascii_array['Ì'] = 'u';
|
||||
to_ascii_array['û'] = 'u';
|
||||
to_ascii_array['Ã'] = 'U';
|
||||
to_ascii_array['Ã'] = 'U';
|
||||
to_ascii_array['Ã'] = 'U';
|
||||
to_ascii_array['Ã'] = 'U';
|
||||
to_ascii_array['Ê'] = 'ae';
|
||||
to_ascii_array['Ã'] = 'AE';
|
||||
to_ascii_array['Ü'] = 'y';
|
||||
to_ascii_array['ÿ'] = 'y';
|
||||
to_ascii_array['Ã'] = 'SS';
|
||||
to_ascii_array['Ã'] = 'C';
|
||||
to_ascii_array['ç'] = 'c';
|
||||
to_ascii_array['Ã'] = 'N';
|
||||
to_ascii_array['ñ'] = 'n';
|
||||
to_ascii_array['¢'] = 'c';
|
||||
to_ascii_array['©'] = '(C)';
|
||||
to_ascii_array['®'] = '(R)';
|
||||
to_ascii_array['«'] = '<<';
|
||||
to_ascii_array['»'] = '>>';
|
||||
|
||||
function toAscii(text) {
|
||||
//var text = field.value;
|
||||
var output = '';
|
||||
|
||||
for (position=0; position < text.length; position++) {
|
||||
var tmp = text.substring(position,position+1);
|
||||
|
||||
if (to_ascii_array[tmp] !== undefined)
|
||||
tmp = to_ascii_array[tmp];
|
||||
|
||||
output += tmp;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
@ -1,6 +1,45 @@
|
||||
This directory contains language translation files for PLA.
|
||||
This directory contains language translation files for PLA. PLA should automatically detect your language based on your
|
||||
browser configuration, and if the language is not available it will fall back to the language used internally (English).
|
||||
|
||||
Language files named by 2 letter iso language name (suffixed with .json)
|
||||
represent the translations for that language.
|
||||
Language files are named by 2 letter iso language name (suffixed with .json) represent the translations for that
|
||||
language.
|
||||
Where a language is spoken in multiple countries, but has local country differences (eg: `en-US` vs `en-GB`,
|
||||
or `zh-CN` vs `zh-TW`), then the language filename is suffixed with `-` and a two letter country, eg:
|
||||
|
||||
eg: en.json
|
||||
* `en.json` for English (General),
|
||||
* `en-GB.json` for English (Great Britain),
|
||||
* `zh-CN.json` for Chinese (China),
|
||||
* `zh-TW.json` for Chinese (Taiwan), etc
|
||||
|
||||
The language file `zz.json` is an example language file, with each translated string prefixed with the letter "Z". Its
|
||||
used to identify any default language text (in english) that is not in a translated configuration. Text strings enclosed
|
||||
in `@lang()`, or `__()` functions are translatable to other languages.
|
||||
|
||||
If you want to update the language text for your language, then:
|
||||
|
||||
* If your language file **exists** (eg: `fr.json` for French), then:
|
||||
* Identify the missing tags (compare it to `zz.json`),
|
||||
* Insert the missing tags into the language file (eg: `fr.json` for French) - ensure you keep the file in English
|
||||
Alphabetical order.
|
||||
|
||||
* If your language file **doesnt** exist (eg; `fr.json` for French), then
|
||||
* Copy the default language file `zz.json` to `fr.json`
|
||||
* Translate the strings
|
||||
|
||||
The structure of the json files is:
|
||||
|
||||
```json
|
||||
{
|
||||
"Untranslated string1": "Translated string1",
|
||||
"Untranslated string2": "Translated string2"
|
||||
}
|
||||
```
|
||||
|
||||
Some important notes:
|
||||
* `Untranslated string` is the string as it appears in PLA, wrapped in either a `__()` or `@lang()` function, normally and english phrase
|
||||
* `Translated string` is the translation for your language
|
||||
* Each translated string must be comma terminated *EXCEPT* the last string
|
||||
|
||||
Please submit a pull request with your translations, so that others users can benefit from the translation.
|
||||
|
||||
If you find any strings that you are not translatable, or translated incorrectly, please submit a bug report.
|
||||
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"Email": "DEV:Email",
|
||||
"Home": "DEV:Home",
|
||||
"Password": "DEV:Password",
|
||||
"Please enter your email": "DEV:Please enter your email",
|
||||
"Please enter your password": "DEV:Please enter your password",
|
||||
"Server Info": "DEV:Server Info",
|
||||
"Server Name": "DEV:Server Name",
|
||||
"Sign in to <strong>:server</strong>": "DEV:Sign in to <strong>:server</strong>"
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used during authentication for various
|
||||
| messages that we need to display to the user. You are free to modify
|
||||
| these language lines according to your application's requirements.
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => 'These credentials do not match our records.',
|
||||
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
||||
|
||||
];
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pagination Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used by the paginator library to build
|
||||
| the simple pagination links. You are free to change them to anything
|
||||
| you want to customize your views to better match your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'previous' => '« Previous',
|
||||
'next' => 'Next »',
|
||||
|
||||
];
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are the default lines which match reasons
|
||||
| that are given by the password broker for a password update attempt
|
||||
| has failed, such as for an invalid token or invalid new password.
|
||||
|
|
||||
*/
|
||||
|
||||
'reset' => 'Your password has been reset!',
|
||||
'sent' => 'We have emailed your password reset link!',
|
||||
'throttled' => 'Please wait before retrying.',
|
||||
'token' => 'This password reset token is invalid.',
|
||||
'user' => "We can't find a user with that email address.",
|
||||
|
||||
];
|
@ -1,151 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Validation Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines contain the default error messages used by
|
||||
| the validator class. Some of these rules have multiple versions such
|
||||
| as the size rules. Feel free to tweak each of these messages here.
|
||||
|
|
||||
*/
|
||||
|
||||
'accepted' => 'The :attribute must be accepted.',
|
||||
'active_url' => 'The :attribute is not a valid URL.',
|
||||
'after' => 'The :attribute must be a date after :date.',
|
||||
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
|
||||
'alpha' => 'The :attribute may only contain letters.',
|
||||
'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.',
|
||||
'alpha_num' => 'The :attribute may only contain letters and numbers.',
|
||||
'array' => 'The :attribute must be an array.',
|
||||
'before' => 'The :attribute must be a date before :date.',
|
||||
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
|
||||
'between' => [
|
||||
'numeric' => 'The :attribute must be between :min and :max.',
|
||||
'file' => 'The :attribute must be between :min and :max kilobytes.',
|
||||
'string' => 'The :attribute must be between :min and :max characters.',
|
||||
'array' => 'The :attribute must have between :min and :max items.',
|
||||
],
|
||||
'boolean' => 'The :attribute field must be true or false.',
|
||||
'confirmed' => 'The :attribute confirmation does not match.',
|
||||
'date' => 'The :attribute is not a valid date.',
|
||||
'date_equals' => 'The :attribute must be a date equal to :date.',
|
||||
'date_format' => 'The :attribute does not match the format :format.',
|
||||
'different' => 'The :attribute and :other must be different.',
|
||||
'digits' => 'The :attribute must be :digits digits.',
|
||||
'digits_between' => 'The :attribute must be between :min and :max digits.',
|
||||
'dimensions' => 'The :attribute has invalid image dimensions.',
|
||||
'distinct' => 'The :attribute field has a duplicate value.',
|
||||
'email' => 'The :attribute must be a valid email address.',
|
||||
'ends_with' => 'The :attribute must end with one of the following: :values.',
|
||||
'exists' => 'The selected :attribute is invalid.',
|
||||
'file' => 'The :attribute must be a file.',
|
||||
'filled' => 'The :attribute field must have a value.',
|
||||
'gt' => [
|
||||
'numeric' => 'The :attribute must be greater than :value.',
|
||||
'file' => 'The :attribute must be greater than :value kilobytes.',
|
||||
'string' => 'The :attribute must be greater than :value characters.',
|
||||
'array' => 'The :attribute must have more than :value items.',
|
||||
],
|
||||
'gte' => [
|
||||
'numeric' => 'The :attribute must be greater than or equal :value.',
|
||||
'file' => 'The :attribute must be greater than or equal :value kilobytes.',
|
||||
'string' => 'The :attribute must be greater than or equal :value characters.',
|
||||
'array' => 'The :attribute must have :value items or more.',
|
||||
],
|
||||
'image' => 'The :attribute must be an image.',
|
||||
'in' => 'The selected :attribute is invalid.',
|
||||
'in_array' => 'The :attribute field does not exist in :other.',
|
||||
'integer' => 'The :attribute must be an integer.',
|
||||
'ip' => 'The :attribute must be a valid IP address.',
|
||||
'ipv4' => 'The :attribute must be a valid IPv4 address.',
|
||||
'ipv6' => 'The :attribute must be a valid IPv6 address.',
|
||||
'json' => 'The :attribute must be a valid JSON string.',
|
||||
'lt' => [
|
||||
'numeric' => 'The :attribute must be less than :value.',
|
||||
'file' => 'The :attribute must be less than :value kilobytes.',
|
||||
'string' => 'The :attribute must be less than :value characters.',
|
||||
'array' => 'The :attribute must have less than :value items.',
|
||||
],
|
||||
'lte' => [
|
||||
'numeric' => 'The :attribute must be less than or equal :value.',
|
||||
'file' => 'The :attribute must be less than or equal :value kilobytes.',
|
||||
'string' => 'The :attribute must be less than or equal :value characters.',
|
||||
'array' => 'The :attribute must not have more than :value items.',
|
||||
],
|
||||
'max' => [
|
||||
'numeric' => 'The :attribute may not be greater than :max.',
|
||||
'file' => 'The :attribute may not be greater than :max kilobytes.',
|
||||
'string' => 'The :attribute may not be greater than :max characters.',
|
||||
'array' => 'The :attribute may not have more than :max items.',
|
||||
],
|
||||
'mimes' => 'The :attribute must be a file of type: :values.',
|
||||
'mimetypes' => 'The :attribute must be a file of type: :values.',
|
||||
'min' => [
|
||||
'numeric' => 'The :attribute must be at least :min.',
|
||||
'file' => 'The :attribute must be at least :min kilobytes.',
|
||||
'string' => 'The :attribute must be at least :min characters.',
|
||||
'array' => 'The :attribute must have at least :min items.',
|
||||
],
|
||||
'not_in' => 'The selected :attribute is invalid.',
|
||||
'not_regex' => 'The :attribute format is invalid.',
|
||||
'numeric' => 'The :attribute must be a number.',
|
||||
'password' => 'The password is incorrect.',
|
||||
'present' => 'The :attribute field must be present.',
|
||||
'regex' => 'The :attribute format is invalid.',
|
||||
'required' => 'The :attribute field is required.',
|
||||
'required_if' => 'The :attribute field is required when :other is :value.',
|
||||
'required_unless' => 'The :attribute field is required unless :other is in :values.',
|
||||
'required_with' => 'The :attribute field is required when :values is present.',
|
||||
'required_with_all' => 'The :attribute field is required when :values are present.',
|
||||
'required_without' => 'The :attribute field is required when :values is not present.',
|
||||
'required_without_all' => 'The :attribute field is required when none of :values are present.',
|
||||
'same' => 'The :attribute and :other must match.',
|
||||
'size' => [
|
||||
'numeric' => 'The :attribute must be :size.',
|
||||
'file' => 'The :attribute must be :size kilobytes.',
|
||||
'string' => 'The :attribute must be :size characters.',
|
||||
'array' => 'The :attribute must contain :size items.',
|
||||
],
|
||||
'starts_with' => 'The :attribute must start with one of the following: :values.',
|
||||
'string' => 'The :attribute must be a string.',
|
||||
'timezone' => 'The :attribute must be a valid zone.',
|
||||
'unique' => 'The :attribute has already been taken.',
|
||||
'uploaded' => 'The :attribute failed to upload.',
|
||||
'url' => 'The :attribute format is invalid.',
|
||||
'uuid' => 'The :attribute must be a valid UUID.',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Validation Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify custom validation messages for attributes using the
|
||||
| convention "attribute.rule" to name the lines. This makes it quick to
|
||||
| specify a specific custom language line for a given attribute rule.
|
||||
|
|
||||
*/
|
||||
|
||||
'custom' => [
|
||||
'attribute-name' => [
|
||||
'rule-name' => 'custom-message',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Validation Attributes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used to swap our attribute placeholder
|
||||
| with something more reader friendly such as "E-Mail Address" instead
|
||||
| of "email". This simply helps us make our message more expressive.
|
||||
|
|
||||
*/
|
||||
|
||||
'attributes' => [],
|
||||
|
||||
];
|
143
resources/lang/zz.json
Normal file
143
resources/lang/zz.json
Normal file
@ -0,0 +1,143 @@
|
||||
{
|
||||
"(no description)": "Z(no description)",
|
||||
"(none)": "Z(none)",
|
||||
"(not applicable)": "Z(not applicable)",
|
||||
"(not specified)": "Z(not specified)",
|
||||
"(unknown syntax)": "Z(unknown syntax)",
|
||||
"Add New Attribute": "ZAdd New Attribute",
|
||||
"Add Objectclass": "ZAdd Objectclass",
|
||||
"Add Value": "ZAdd Value",
|
||||
"Aliases": "ZAliases",
|
||||
"Attributes": "ZAttributes",
|
||||
"attributes(s)": "Zattributes(s)",
|
||||
"Attribute Types": "ZAttribute Types",
|
||||
"Authority Key Identifier": "ZAuthority Key Identifier",
|
||||
"Certificate Subject": "ZCertificate Subject",
|
||||
"Check": "ZCheck",
|
||||
"Check Password": "ZCheck Password",
|
||||
"Close": "ZClose",
|
||||
"Collective": "ZCollective",
|
||||
"Copy\/Move": "ZCopy\/Move",
|
||||
"Create Child Entry": "ZCreate Child Entry",
|
||||
"Created": "ZCreated",
|
||||
"Create Entry": "ZCreate Entry",
|
||||
"Create New Entry": "ZCreate New Entry",
|
||||
"Create new LDAP item here": "ZCreate new LDAP item here",
|
||||
"Delete": "ZDelete",
|
||||
"Deleted": "ZDeleted",
|
||||
"Delete Entry": "ZDelete Entry",
|
||||
"Deleting this DN will permanently delete it from your LDAP server.": "ZDeleting this DN will permanently delete it from your LDAP server.",
|
||||
"Description": "ZDescription",
|
||||
"DN": "ZDN",
|
||||
"Download": "ZDownload",
|
||||
"Do you want to make the following changes?": "ZDo you want to make the following changes?",
|
||||
"dynamic": "Zdynamic",
|
||||
"Edit Entry": "ZEdit Entry",
|
||||
"Entry": "ZEntry",
|
||||
"Entry updated": "ZEntry updated",
|
||||
"Equality": "ZEquality",
|
||||
"Error": "ZError",
|
||||
"Expired": "ZExpired",
|
||||
"Expires": "ZExpires",
|
||||
"Export": "ZExport",
|
||||
"Exported by": "ZExported by",
|
||||
"Force as MAY by config": "ZForce as MAY by config",
|
||||
"Generated by": "ZGenerated by",
|
||||
"Home": "ZHome",
|
||||
"Ignoring blank value": "ZIgnoring blank value",
|
||||
"Import Result": "ZImport Result",
|
||||
"Inherits from": "ZInherits from",
|
||||
"Internal": "ZInternal",
|
||||
"Invalid Password": "ZInvalid Password",
|
||||
"KRB_DISALLOW_ALL_TIX": "ZKRB_DISALLOW_ALL_TIX",
|
||||
"KRB_DISALLOW_DUP_SKEY": "ZKRB_DISALLOW_DUP_SKEY",
|
||||
"KRB_DISALLOW_FORWARDABLE": "ZKRB_DISALLOW_FORWARDABLE",
|
||||
"KRB_DISALLOW_POSTDATED": "ZKRB_DISALLOW_POSTDATED",
|
||||
"KRB_DISALLOW_PROXIABLE": "ZKRB_DISALLOW_PROXIABLE",
|
||||
"KRB_DISALLOW_RENEWABLE": "ZKRB_DISALLOW_RENEWABLE",
|
||||
"KRB_DISALLOW_SVR": "ZKRB_DISALLOW_SVR",
|
||||
"KRB_DISALLOW_TGT_BASED": "ZKRB_DISALLOW_TGT_BASED",
|
||||
"KRB_PWCHANGE_SERVICE": "ZKRB_PWCHANGE_SERVICE",
|
||||
"KRB_REQUIRES_HW_AUTH": "ZKRB_REQUIRES_HW_AUTH",
|
||||
"KRB_REQUIRES_PRE_AUTH": "ZKRB_REQUIRES_PRE_AUTH",
|
||||
"KRB_REQUIRES_PWCHANGE": "ZKRB_REQUIRES_PWCHANGE",
|
||||
"LDAP Authentication Error": "ZLDAP Authentication Error",
|
||||
"LDAP Entry": "ZLDAP Entry",
|
||||
"LDAP Server Error Code": "ZLDAP Server Error Code",
|
||||
"LDAP Server Unavailable": "ZLDAP Server Unavailable",
|
||||
"LDIF": "ZLDIF",
|
||||
"LDIF Import": "ZLDIF Import",
|
||||
"LDIF Import Result": "ZLDIF Import Result",
|
||||
"Line": "ZLine",
|
||||
"locale": "ZZ",
|
||||
"Matching Rules": "ZMatching Rules",
|
||||
"Maximum file size": "ZMaximum file size",
|
||||
"Maximum Length": "ZMaximum Length",
|
||||
"NEW": "ZNEW",
|
||||
"New Value": "ZNew Value",
|
||||
"Next": "ZNext",
|
||||
"No attributes changed": "ZNo attributes changed",
|
||||
"No description available, can you help with one?": "ZNo description available, can you help with one?",
|
||||
"No Server Name Yet": "ZNo Server Name Yet",
|
||||
"NOT DEFINED": "ZNOT DEFINED",
|
||||
"Not Implemented": "ZNot Implemented",
|
||||
"Object Classes": "ZObject Classes",
|
||||
"Object Identifier": "ZObject Identifier",
|
||||
"Obsolete": "ZObsolete",
|
||||
"Optional Attributes": "ZOptional Attributes",
|
||||
"Ordering": "ZOrdering",
|
||||
"Or upload LDIF file": "ZOr upload LDIF file",
|
||||
"Parent to": "ZParent to",
|
||||
"Paste in your LDIF here": "ZPaste in your LDIF here",
|
||||
"Possible Causes": "ZPossible Causes",
|
||||
"Process": "ZProcess",
|
||||
"rdn": "Zrdn",
|
||||
"RDN is required": "ZRDN is required",
|
||||
"RDN is required.": "ZRDN is required.",
|
||||
"RDN value is required.": "ZRDN value is required.",
|
||||
"Rename": "ZRename",
|
||||
"Replace": "ZReplace",
|
||||
"required": "Zrequired",
|
||||
"Required Attribute by ObjectClass(es)": "ZRequired Attribute by ObjectClass(es)",
|
||||
"Required Attributes": "ZRequired Attributes",
|
||||
"Required by ObjectClasses": "ZRequired by ObjectClasses",
|
||||
"Reset": "ZReset",
|
||||
"Result": "ZResult",
|
||||
"Schema Information": "ZSchema Information",
|
||||
"Search Filter": "ZSearch Filter",
|
||||
"Search Scope": "ZSearch Scope",
|
||||
"Select a Structural ObjectClass...": "ZSelect a Structural ObjectClass...",
|
||||
"Select attribute...": "ZSelect attribute...",
|
||||
"Select from": "ZSelect from",
|
||||
"Serial Number": "ZSerial Number",
|
||||
"Server": "ZServer",
|
||||
"Server Info": "ZServer Info",
|
||||
"Single Valued": "ZSingle Valued",
|
||||
"Step": "ZStep",
|
||||
"structural": "Zstructural",
|
||||
"Subject Key Identifier": "ZSubject Key Identifier",
|
||||
"Substring Rule": "ZSubstring Rule",
|
||||
"Syntax": "ZSyntax",
|
||||
"Syntaxes": "ZSyntaxes",
|
||||
"These are dynamic values present as a result of another attribute": "ZThese are dynamic values present as a result of another attribute",
|
||||
"This attribute is required for the RDN": "ZThis attribute is required for the RDN",
|
||||
"To Server": "ZTo Server",
|
||||
"Total Entries": "ZTotal Entries",
|
||||
"Type": "ZType",
|
||||
"Unknown": "ZUnknown",
|
||||
"Untrapped Error": "ZUntrapped Error",
|
||||
"Update": "ZUpdate",
|
||||
"Updated": "ZUpdated",
|
||||
"Upload JpegPhoto": "ZUpload JpegPhoto",
|
||||
"Usage": "ZUsage",
|
||||
"Used by Attributes": "ZUsed by Attributes",
|
||||
"Used by ObjectClasses": "ZUsed by ObjectClasses",
|
||||
"User Modification": "ZUser Modification",
|
||||
"Validation Errors": "ZValidation Errors",
|
||||
"Version": "ZVersion",
|
||||
"WARNING": "ZWARNING",
|
||||
"Your DNS server cannot resolve that hostname": "ZYour DNS server cannot resolve that hostname",
|
||||
"Your LDAP server hostname is incorrect": "ZYour LDAP server hostname is incorrect",
|
||||
"Your LDAP server is not connectable": "ZYour LDAP server is not connectable",
|
||||
"Your Resolver is not pointing to your DNS server": "ZYour Resolver is not pointing to your DNS server"
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html translate="no">
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" translate="no">
|
||||
@section('htmlheader')
|
||||
@include('architect::layouts.partials.htmlheader')
|
||||
@show
|
||||
|
@ -18,3 +18,8 @@
|
||||
<!-- Any Custom JS -->
|
||||
<script src="{{ asset('js/custom.js') }}"></script>
|
||||
@endif
|
||||
|
||||
@if(file_exists('js/template.js'))
|
||||
<!-- Template Engine JS -->
|
||||
<script src="{{ asset('js/template.js') }}"></script>
|
||||
@endif
|
@ -38,7 +38,7 @@
|
||||
<span></span>
|
||||
<div id="searching" class="d-none"><i class="fas fa-fw fa-spinner fa-pulse text-light"></i></div>
|
||||
</button>
|
||||
<div id="search_results" class="overflow-scroll" style="height: 300px;"></div>
|
||||
<div id="search_results"></div>
|
||||
</div>
|
||||
<button class="btn-close"></button>
|
||||
</div>
|
||||
@ -164,18 +164,14 @@
|
||||
</div>
|
||||
|
||||
@section('page-scripts')
|
||||
<style>
|
||||
#search_results ul.typeahead.dropdown-menu {
|
||||
overflow: scroll;
|
||||
max-height: 300px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('button[id^="link-"]').on('click',function(item) {
|
||||
var content;
|
||||
|
||||
// Remove our fancy-tree highlight, since we are rendering the frame
|
||||
$('.fancytree-node.fancytree-active').removeClass('fancytree-active');
|
||||
|
||||
$.ajax({
|
||||
url: $(this).data('link'),
|
||||
method: 'GET',
|
||||
|
@ -6,7 +6,22 @@
|
||||
<div class="col-12 bg-light text-dark p-2 rounded-2">
|
||||
<span class="d-flex justify-content-between">
|
||||
<span style="width: 20em;">
|
||||
<strong class="align-middle"><abbr title="{{ $o->description }}">{{ $o->name }}</abbr></strong>
|
||||
<strong class="align-middle"><abbr title="{{ (($x=$template?->attributeTitle($o->name)) ? $o->name.': ' : '').$o->description }}">{{ $x ?: $o->name }}</abbr></strong>
|
||||
@if($new)
|
||||
@if($template?->attributeReadOnly($o->name_lc))
|
||||
<sup data-bs-toggle="tooltip" title="@lang('Input disabled')"><i class="fas fa-ban"></i></sup>
|
||||
@endif
|
||||
@if($ca=$template?->onChangeAttribute($o->name_lc))
|
||||
<sup data-bs-toggle="tooltip" title="@lang('Value triggers an update to another attribute')"><i class="fas fa-keyboard"></i></sup>
|
||||
@endif
|
||||
@if ($ct=$template?->onChangeTarget($o->name_lc))
|
||||
<sup data-bs-toggle="tooltip" title="@lang('Value calculated from another attribute')"><i class="fas fa-wand-magic-sparkles"></i></sup>
|
||||
@endif
|
||||
@if((! $ca) && (! $ct) && $template?->attribute($o->name_lc))
|
||||
<sup data-bs-toggle="tooltip" title="@lang('Value controlled by template')"><i class="fas fa-wand-magic"></i></sup>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if($o->hints->count())
|
||||
<sup>
|
||||
[
|
||||
@ -47,7 +62,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-attribute :o="$o" :edit="$edit" :new="$new" :updated="$updated"/>
|
||||
@switch($template?->attributeType($o->name))
|
||||
@case('select')
|
||||
<x-attribute.template.select :o="$o" :template="$template" :edit="(! $template?->attributeReadOnly($o->name)) && $edit" :new="$new"/>
|
||||
@break;
|
||||
|
||||
@default
|
||||
<x-attribute :o="$o" :edit="(! $template?->attributeReadOnly($o->name)) && $edit" :new="$new" :updated="$updated"/>
|
||||
@endswitch
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -5,20 +5,32 @@
|
||||
@foreach($o->langtags as $langtag)
|
||||
<span @class(['tab-pane','active'=>$loop->index === 0]) id="langtag-{{ $o->name_lc }}-{{ $langtag }}" role="tabpanel">
|
||||
@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),'bg-success-subtle'=>$updated]) 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>
|
||||
</div>
|
||||
|
||||
@else
|
||||
<input type="text" @class(['form-control','mb-1','bg-success-subtle'=>$updated]) value="{{ $value }}" disabled>
|
||||
<!-- AutoValue Lock -->
|
||||
@if($new && $template && ($av=$template->attributeValue($o->name_lc)))
|
||||
<input type="hidden" name="_auto_value[{{ $o->name_lc }}]" value="{{ $av }}">
|
||||
@endif
|
||||
|
||||
<div class="input-group has-validation">
|
||||
<input type="text"
|
||||
@class([
|
||||
'form-control',
|
||||
'noedit'=>(! $edit) || ($o->is_rdn),
|
||||
'is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)) || ($e=$errors->get('_auto_value.'.$o->name_lc)),
|
||||
'mb-1',
|
||||
'border-focus'=>! ($tv=$o->tagValuesOld($langtag))->contains($value),
|
||||
'bg-success-subtle'=>$updated])
|
||||
name="{{ $o->name_lc }}[{{ $langtag }}][]"
|
||||
value="{{ $value ?: ($av ?? '') }}"
|
||||
placeholder="{{ ! is_null($x=$tv->get($loop->index)) ? $x : '['.__('NEW').']' }}"
|
||||
readonly
|
||||
@disabled($o->isDynamic())>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e)
|
||||
{{ join('|',$e) }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</span>
|
||||
@endforeach
|
||||
@ -34,3 +46,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</x-attribute.layout>
|
||||
|
||||
@if($new && ($x=$template?->onChange($o->name))?->count())
|
||||
@section('page-scripts')
|
||||
<!-- START: ONCHANGE PROCESSING {{ $o->name }} -->
|
||||
<script type="text/javascript">
|
||||
$('#{{ $o->name_lc }}').on('change',function() {
|
||||
{!! $x->join('') !!}
|
||||
});
|
||||
</script>
|
||||
<!-- END: ONCHANGE PROCESSING {{ $o->name }} -->
|
||||
@append
|
||||
@endif
|
@ -1,6 +1,5 @@
|
||||
<div class="row pt-2">
|
||||
<div @class(['col-1','d-none'=>(! $edit) && (! ($detail ?? FALSE))])></div>
|
||||
<div class="col-10">
|
||||
<div class="col-10 offset-1">
|
||||
<attribute id="{{ $o->name_lc }}">
|
||||
{{ $slot }}
|
||||
</attribute>
|
||||
|
@ -3,8 +3,8 @@
|
||||
@foreach($o->langtags as $langtag)
|
||||
@foreach(($o->tagValues($langtag)->count() ? $o->tagValues($langtag) : [$langtag => NULL]) as $key => $value)
|
||||
@if($edit)
|
||||
<div class="input-group has-validation mb-3">
|
||||
<x-form.select id="userpassword_hash_{{$loop->index}}" name="userpassword_hash[{{ $langtag }}][]" :value="$o->hash($new ? '' : $value)->id()" :options="$helpers" allowclear="false" :disabled="! $new"/>
|
||||
<div class="input-group has-validation">
|
||||
<x-form.select id="userpassword_hash_{{$loop->index}}{{$template?->name ?: ''}}" name="_userpassword_hash[{{ $langtag }}][]" :value="old('_userpassword_hash.'.$langtag.'.0',$o->hash($new ? '' : ($value ?? ''))->id())" :options="$helpers" allowclear="false" :disabled="! $new"/>
|
||||
<input type="password" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)),'mb-1','border-focus'=>! $o->tagValuesOld($langtag)->contains($value),'bg-success-subtle'=>$updated]) name="{{ $o->name_lc }}[{{ $langtag }}][]" value="{{ Arr::get(old($o->name_lc),$langtag.'.'.$loop->index,$value ? md5($value) : '') }}" @readonly(! $new)>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@ -20,11 +20,11 @@
|
||||
@endforeach
|
||||
</x-attribute.layout>
|
||||
|
||||
@if($edit)
|
||||
@if($edit && $o->tagValuesOld($langtag)->dot()->filter()->count())
|
||||
<div class="row">
|
||||
<div class="offset-1 col-4 p-2">
|
||||
<div class="offset-1 col-4">
|
||||
<span class="p-0 m-0">
|
||||
<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>
|
||||
<button name="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>
|
||||
|
@ -3,24 +3,24 @@
|
||||
@foreach(($o->values->count() ? $o->values : [NULL]) 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">
|
||||
<select @class(['form-select','is-invalid'=>$errors->get('_rdn')]) id="rdn" name="_rdn">
|
||||
<option value=""></option>
|
||||
|
||||
@foreach($o->attrs->map(fn($item)=>['id'=>$item,'value'=>$item]) as $option)
|
||||
@continue(! Arr::get($option,'value'))
|
||||
<option value="{{ Arr::get($option,'id') }}" @selected(Arr::get($option,'id') == old('rdn',$value ?? ''))>{{ Arr::get($option,'value') }}</option>
|
||||
<option value="{{ strtolower(Arr::get($option,'id')) }}" @selected(Arr::get($option,'id') == old('_rdn',$value ?? ''))>{{ Arr::get($option,'value') }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<span class="input-group-text">=</span>
|
||||
<input type="text" @class(['form-control','is-invalid'=>$errors->get('rdn_value')]) id="rdn_value" name="rdn_value" value="{{ old('rdn_value') }}" placeholder="rdn">
|
||||
<input type="text" @class(['form-control','is-invalid'=>$errors->get('_rdn_value')]) id="rdn_value" name="_rdn_value" value="{{ old('_rdn_value') }}" placeholder="rdn">
|
||||
<label class="input-group-text" for="inputGroupSelect02">,{{ $o->base }}</label>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@error('rdn')
|
||||
@error('_rdn')
|
||||
{{ $message }}
|
||||
@enderror
|
||||
@error('rdn_value')
|
||||
@error('_rdn_value')
|
||||
{{ $message }}
|
||||
@enderror
|
||||
</div>
|
||||
|
@ -0,0 +1,28 @@
|
||||
<!-- $o=Attribute::class -->
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o">
|
||||
@foreach($o->langtags as $langtag)
|
||||
@foreach(($o->tagValues($langtag)->count() ? $o->tagValues($langtag) : [$langtag => NULL]) as $key => $value)
|
||||
@if($edit)
|
||||
<div class="select-group">
|
||||
<x-form.select
|
||||
@class(['is-invalid'=>($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index)),'mb-1','border-focus'=>! $o->tagValuesOld($langtag)->contains($value)])
|
||||
id="{{ $o->name_lc }}_{{$loop->index}}{{$template?->name ?: ''}}"
|
||||
name="{{ $o->name_lc }}[{{ $langtag }}][]"
|
||||
:value="$value"
|
||||
:options="$template->attributeOptions($o->name_lc)"
|
||||
allowclear="true"
|
||||
:disabled="! $new"
|
||||
:readonly="false"/>
|
||||
|
||||
<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
|
||||
@endforeach
|
||||
</x-attribute.layout>
|
@ -38,7 +38,7 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<x-form.select id="newoc" label="Select from..."/>
|
||||
<x-form.select id="newoc" :label="__('Select from').'...'"/>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
|
@ -1,4 +1,4 @@
|
||||
<button id="form-reset" class="btn btn-outline-danger">@lang('Reset')</button>
|
||||
<button id="form-reset" class="btn btn-sm btn-outline-danger">@lang('Reset')</button>
|
||||
|
||||
@section('page-scripts')
|
||||
<script>
|
||||
|
@ -2,27 +2,28 @@
|
||||
@isset($name)
|
||||
<input type="hidden" id="{{ $id ?? $name }}_disabled" name="{{ $name }}" value="" disabled>
|
||||
@endisset
|
||||
<select class="form-select @isset($name)@error((! empty($old)) ? $old : ($id ?? $name)) is-invalid @enderror @endisset" id="{{ $id ?? $name }}" @isset($name)name="{{ $name }}"@endisset @required(isset($required) && $required) @disabled(isset($disabled) && $disabled)>
|
||||
@if((empty($value) && ! empty($options)) || isset($addnew) || isset($choose))
|
||||
|
||||
<select class="form-select @error($old ?? $id ?? $name) is-invalid @enderror" id="{{ $id ?? $name}}" @isset($name)name="{{ $name }}"@endisset @required($required ?? FALSE) @disabled($disabled ?? FALSE)>
|
||||
@if((empty($value) && ! empty($options)) || isset($addnew))
|
||||
<option value=""></option>
|
||||
|
||||
@isset($addnew)
|
||||
<option value="new">{{ $addnew ?: 'Add New' }}</option>
|
||||
@endisset
|
||||
@endif
|
||||
|
||||
@isset($options)
|
||||
@empty($groupby)
|
||||
@foreach($options as $option)
|
||||
@continue(! Arr::get($option,'value'))
|
||||
<option value="{{ Arr::get($option,'id') }}" @selected(isset($name) && (Arr::get($option,'id') == old($old ?? $name,$value ?? '')))>{{ Arr::get($option,'value') }}</option>
|
||||
<option value="{{ Arr::get($option,'id') }}" @selected(Arr::get($option,'id') == collect(old())->dot()->get(isset($old) ? $old.'.0' : ($id ?? $name),$value ?? ''))>{{ Arr::get($option,'value') }}</option>
|
||||
@endforeach
|
||||
|
||||
@else
|
||||
@foreach($options->groupBy($groupby) as $group)
|
||||
<optgroup label="{{ $groupby == 'active' ? (Arr::get($group->first(),$groupby) ? 'Active' : 'Not Active') : Arr::get($group->first(),$groupby) }}">
|
||||
<optgroup label="{{ Arr::get($group->first(),$groupby) }}">
|
||||
@foreach($group as $option)
|
||||
@continue(! Arr::get($option,'value'))
|
||||
<option value="{{ Arr::get($option,'id') }}" @selected(isset($name) && (Arr::get($option,'id') == old($old ?? $name,$value ?? '')))>{{ Arr::get($option,'value') }}</option>
|
||||
<option value="{{ Arr::get($option,'id') }}" @selected(Arr::get($option,'id') == collect(old())->dot()->get(isset($old) ? $old.'.0' : ($id ?? $name),$value ?? ''))>{{ Arr::get($option,'value') }}</option>
|
||||
@endforeach
|
||||
</optgroup>
|
||||
@endforeach
|
||||
|
@ -3,7 +3,7 @@
|
||||
<!-- $o=Certificate::class -->
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o">
|
||||
@foreach($o->tagValuesOld('binary') as $key => $value)
|
||||
<!-- If this attribute is not handle, it'll be an Attribute::class, we'll just render it normally -->
|
||||
<!-- If this attribute is not handled, it'll be an Attribute::class, we'll just render it normally -->
|
||||
@if(($o instanceof Certificate) && $edit)
|
||||
<input type="hidden" name="name={{ $o->name_lc }}[binary][]" value="{{ md5($value) }}">
|
||||
|
||||
@ -16,9 +16,29 @@
|
||||
@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 class="input-helper small">
|
||||
<table class="table table-borderless w-75">
|
||||
<tr >
|
||||
<td class="p-0">@lang('Certificate Subject')</td>
|
||||
<th class="p-0">{{ $o->subject($loop->index) }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="p-0">{{ ($expire=$o->expires($loop->index))->isPast() ? __('Expired') : __('Expires') }}</td>
|
||||
<th class="p-0">{{ $expire->format(config('pla.datetime_format','Y-m-d H:i:s')) }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="p-0">@lang('Serial Number')</td>
|
||||
<th class="p-0">{{ $o->cert_info('serialNumberHex',$loop->index) }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="p-0">@lang('Subject Key Identifier')</td>
|
||||
<th class="p-0">{{ $o->subject_key_identifier($loop->index) }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="p-0">@lang('Authority Key Identifier')</td>
|
||||
<th class="p-0">{{ $o->authority_key_identifier($loop->index) }}</th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@else
|
||||
|
@ -1,7 +1,14 @@
|
||||
<!-- $o=CertificateList::class -->
|
||||
<x-attribute.layout :edit="$edit" :new="$new" :o="$o">
|
||||
@foreach($o->tagValuesOld('binary') as $key => $value)
|
||||
<!-- If this attribute is not handle, it'll be an Attribute::class, we'll just render it normally -->
|
||||
<span class="form-control mb-1"><pre class="m-0">{{ $o->render_item_old('binary.'.$key) }}</pre></span>
|
||||
<div class="input-group has-validation mb-3">
|
||||
<textarea class="form-control mb-1 font-size-md font-monospace overflow-hidden" rows="{{ count(explode("\n",$x=$o->render_item_old('binary.'.$key))) }}" disabled>{{ $x }}</textarea>
|
||||
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e=$errors->get($o->name_lc.'.binary.'.$loop->index))
|
||||
{{ join('|',$e) }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</x-attribute.layout>
|
@ -24,7 +24,7 @@
|
||||
<td>BaseDN(s)</td>
|
||||
<td>
|
||||
<table class="table table-sm table-borderless">
|
||||
@foreach($server->baseDNs(TRUE)->sort(fn($item)=>$item->sort_key) as $item)
|
||||
@foreach($server->baseDNs() as $item)
|
||||
<tr>
|
||||
<td class="ps-0">{{ $item->getDn() }}</td>
|
||||
</tr>
|
||||
@ -44,6 +44,12 @@
|
||||
<td>Root URL</td>
|
||||
<td>{{ request()->root() }}</td>
|
||||
</tr>
|
||||
|
||||
<!-- Locale -->
|
||||
<tr>
|
||||
<td>Locale</td>
|
||||
<td>{{ __('locale') }} [{{ config('app.locale') }}]</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
33
resources/views/errors/501.blade.php
Normal file
33
resources/views/errors/501.blade.php
Normal file
@ -0,0 +1,33 @@
|
||||
@extends('architect::layouts.error')
|
||||
|
||||
@section('error')
|
||||
501: @lang('LDAP User Error')
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<table class="table table-sm table-borderless table-condensed">
|
||||
<tr>
|
||||
<th>@lang('Error')</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">{{ $exception->getMessage() }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>@lang('Possible Causes')</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul class="ps-3">
|
||||
<li>The DN you used to login actually doesnt exist in the server (DN's must exist in order to login)</li>
|
||||
<li>You are attempting to use the <strong>rootdn</strong> to login (not supported)</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>To suppress this message, set <strong>LDAP_ALERT_ROOTDN</strong> to <strong>FALSE</strong> before starting PLA.</p>
|
||||
<p>Back to <a href="{{ url('login') }}">login</a>?</p>
|
||||
|
||||
@endsection
|
@ -8,13 +8,13 @@
|
||||
<div class="d-none round" id="newattr-select">
|
||||
<div class="row">
|
||||
<div class="col-12 bg-dark text-light p-2 rounded-2">
|
||||
<i class="fas fa-plus-circle"></i> Add New Attribute
|
||||
<i class="fas fa-plus-circle"></i> @lang('Add New Attribute')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 pt-2">
|
||||
<x-form.select id="newattr" label="Select from..." :options="$o->getMissingAttributes()->sortBy('name')->unique('name')->map(fn($item)=>['id'=>$item->name,'value'=>$item->name])"/>
|
||||
<x-form.select id="newattr" :label="__('Select from').'...'" :options="$o->getMissingAttributes()->sortBy('name')->unique('name')->map(fn($item)=>['id'=>$item->name,'value'=>$item->name])"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,19 +2,19 @@
|
||||
<div class="col-12 col-xl-3">
|
||||
<select id="attributetype" class="form-control">
|
||||
<option value="-all-">-all-</option>
|
||||
@foreach($attributetypes as $o)
|
||||
<option value="{{ $o->name_lc }}">{{ $o->name }}</option>
|
||||
@foreach(($at=$attributetypes->sortBy(fn($item)=>$item->names_lc->join(','))) as $o)
|
||||
<option value="{{ $o->names_lc->join('-') }}">{{ $o->names->join(',') }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-xl-9">
|
||||
@foreach($attributetypes as $o)
|
||||
<span id="at-{{ $o->name_lc }}">
|
||||
@foreach($at as $o)
|
||||
<span id="at-{{ $o->names_lc->join('-') }}">
|
||||
<table class="schema table table-sm table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-dark" colspan="2">{{ $o->name }}<span class="float-end"><abbr title="{{ $o->line }}"><i class="fas fa-fw fa-file-contract"></i></abbr></span></th>
|
||||
<th class="table-dark" colspan="2">{{ $o->names->join(' / ') }}<span class="float-end"><abbr title="{{ $o->line }}"><i class="fas fa-fw fa-file-contract"></i></abbr></span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
<td>@lang('Substring Rule')</td><td><strong>{{ $o->sub_str_rule ?: __('(not specified)') }}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('Syntax')</td><td><strong>{{ ($o->syntax_oid && $x=$server->schemaSyntaxName($o->syntax_oid)) ? $x->description : __('(unknown syntax)') }} @if($o->syntax_oid)({{ $o->syntax_oid }})@endif</strong></td>
|
||||
<td>@lang('Syntax')</td><td><strong>{{ ($o->syntax_oid && $x=$server->get_syntax($o->syntax_oid)) ? $x->description : __('(unknown syntax)') }} @if($o->syntax_oid)({{ $o->syntax_oid }})@endif</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('Single Valued')</td><td><strong>@lang($o->is_single_value ? 'Yes' : 'No')</strong></td>
|
||||
@ -77,11 +77,8 @@
|
||||
<tr>
|
||||
<td>@lang('Aliases')</td>
|
||||
<td><strong>
|
||||
@if($o->aliases->count())
|
||||
@foreach($o->aliases as $alias)
|
||||
@if($loop->index)</strong> <strong>@endif
|
||||
<a class="attributetype" id="{{ strtolower($alias) }}" href="#{{ strtolower($alias) }}">{{ $alias }}</a>
|
||||
@endforeach
|
||||
@if($o->names->count() > 1)
|
||||
{!! $o->names->join('</strong>, <strong>') !!}
|
||||
@else
|
||||
@lang('(none)')
|
||||
@endif
|
||||
|
@ -35,10 +35,10 @@
|
||||
<td>@lang('Inherits from')</td>
|
||||
<td colspan="3">
|
||||
<strong>
|
||||
@if($o->sup->count() === 0)
|
||||
@if($o->sup_classes->count() === 0)
|
||||
@lang('(none)')
|
||||
@else
|
||||
@foreach($o->sup as $sup)
|
||||
@foreach($o->sup_classes as $sup)
|
||||
@if($loop->index)</strong> <strong>@endif
|
||||
<a class="objectclass" id="{{ strtolower($sup) }}" href="#{{ strtolower($sup) }}">{{ $sup }}</a>
|
||||
@endforeach
|
||||
@ -53,10 +53,10 @@
|
||||
<strong>
|
||||
@if(strtolower($o->name) === 'top')
|
||||
<a class="objectclass" id="-all-">(all)</a>
|
||||
@elseif(! $o->getChildObjectClasses()->count())
|
||||
@elseif(! $o->child_classes->count())
|
||||
@lang('(none)')
|
||||
@else
|
||||
@foreach($o->getChildObjectClasses() as $childoc)
|
||||
@foreach($o->child_classes as $childoc)
|
||||
@if($loop->index)</strong> <strong>@endif
|
||||
<a class="objectclass" id="{{ strtolower($childoc) }}" href="#{{ strtolower($childoc) }}">{{ $childoc }}</a>
|
||||
@endforeach
|
||||
|
16
resources/views/fragment/template/dn.blade.php
Normal file
16
resources/views/fragment/template/dn.blade.php
Normal file
@ -0,0 +1,16 @@
|
||||
<!-- $template=Template -->
|
||||
<form id="template-edit" method="POST" class="needs-validation" action="{{ url('entry/update/pending') }}" novalidate readonly>
|
||||
@csrf
|
||||
|
||||
<input type="hidden" name="dn" value="">
|
||||
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
@php($up=(session()->get('updated') ?: collect()))
|
||||
|
||||
@foreach($o->getVisibleAttributes()->filter(fn($item)=>$template->attributes->keys()->map('strtolower')->contains($item->name_lc)) as $ao)
|
||||
<x-attribute-type :o="$ao" :edit="true" :new="false" :template="$template" :updated="$up->contains($ao->name)"/>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -3,9 +3,3 @@
|
||||
@section('main-content')
|
||||
@include('frames.'.$subframe)
|
||||
@endsection
|
||||
|
||||
@section('page-scripts')
|
||||
<script type="text/javascript">
|
||||
var basedn = {!! $bases->toJson() !!};
|
||||
</script>
|
||||
@append
|
@ -17,52 +17,72 @@
|
||||
<div class="main-card mb-3 card">
|
||||
|
||||
<div class="card-header">
|
||||
@lang('Create New Entry') - @lang('Step') {{ $step }}
|
||||
@lang('Create New Entry') - @lang('Step') {{ $step }} @if($template) <span class="ms-auto"><i class="fa fa-fw {{ $template->icon }}"></i> {{ $template->title }}</span>@endif
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form id="dn-create" method="POST" class="needs-validation" action="{{ url((int)$step === 2 ? 'entry/create' : 'entry/add') }}" enctype="multipart/form-data" novalidate>
|
||||
@csrf
|
||||
|
||||
<input type="hidden" name="key" value="{{ Crypt::encryptString('*create|'.$container) }}">
|
||||
<input type="hidden" name="step" value="{{ $step }}">
|
||||
<input type="hidden" name="_key" value="{{ Crypt::encryptString('*create|'.$container) }}">
|
||||
<input type="hidden" name="_step" value="{{ $step }}">
|
||||
|
||||
@switch($step)
|
||||
@case(1)
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="col-12 col-md-5">
|
||||
<x-form.select
|
||||
id="objectclass"
|
||||
name="objectclass[{{ Entry::TAG_NOTAG }}][]"
|
||||
old="objectclass.{{ Entry::TAG_NOTAG }}"
|
||||
:label="__('Select a Structural ObjectClass...')"
|
||||
:label="__('Select a Structural ObjectClass').'...'"
|
||||
: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])"
|
||||
multiple="false"
|
||||
:allowclear="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@if($o->templates->count())
|
||||
<div class="col-md-1">
|
||||
<strong>@lang('OR')</strong>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-5">
|
||||
<x-form.select
|
||||
name="template"
|
||||
:label="__('Select a Template').'...'"
|
||||
:options="$o->templates
|
||||
->map(fn($item,$key)=>['id'=>$key,'value'=>$item->title])"
|
||||
:allowclear="true"
|
||||
/>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@break
|
||||
|
||||
@case(2)
|
||||
<x-attribute-type :o="$o->getObject('rdn')" :edit="TRUE" :new="FALSE" :updated="FALSE"/>
|
||||
<input type="hidden" name="_template" value="{{ $template?->file }}">
|
||||
<x-attribute-type :o="$o->getObject('rdn')" :edit="true" :new="true" :template="$template" :updated="false"/>
|
||||
|
||||
@foreach($o->getVisibleAttributes() as $ao)
|
||||
<x-attribute-type :o="$ao" :edit="TRUE" :new="FALSE" :updated="FALSE"/>
|
||||
<x-attribute-type :o="$ao" :edit="true" :new="true" :template="$template" :updated="false"/>
|
||||
@endforeach
|
||||
|
||||
@include('fragment.dn.add_attr')
|
||||
|
||||
@if(! $template)
|
||||
<!-- @todo When we come back from validation the javascript to append a new attribute is not loaded -->
|
||||
<!-- @todo When we render attributes with javascript, the javascript is not loaded -->
|
||||
@include('fragment.dn.add_attr')
|
||||
@endif
|
||||
@break;
|
||||
@endswitch
|
||||
</form>
|
||||
|
||||
<div class="row d-none pt-3">
|
||||
<div class="col-12 {{ $step > 1 ? 'offset-sm-2' : '' }} col-lg-10">
|
||||
<div class="col-11 {{ $step > 1 ? 'text-end' : '' }} pe-0">
|
||||
<x-form.reset form="dn-create"/>
|
||||
<x-form.submit action="Next" form="dn-create"/>
|
||||
<x-form.submit :action="__('Next')" form="dn-create"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,7 +92,6 @@
|
||||
@endsection
|
||||
|
||||
@section('page-scripts')
|
||||
|
||||
<script type="text/javascript">
|
||||
var rdn_attr;
|
||||
|
||||
@ -87,6 +106,10 @@
|
||||
if (rdn_attr && ($(this)[0].name === rdn_attr+'[]'))
|
||||
return;
|
||||
|
||||
// Exclude attributes marked as noedit
|
||||
if ($(this).hasClass('noedit'))
|
||||
return;
|
||||
|
||||
$(this).attr('readonly',false);
|
||||
});
|
||||
|
||||
@ -102,7 +125,15 @@
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
@if($step === 2)
|
||||
@if($step === 1)
|
||||
$('#objectclass').on('select2:open',function(){
|
||||
$('#template').val(null).trigger('change');
|
||||
});
|
||||
|
||||
$('#template').on('select2:open',function(){
|
||||
$('#objectclass').val(null).trigger('change');
|
||||
})
|
||||
@elseif($step === 2)
|
||||
$('#newattr').on('change',function(item) {
|
||||
var oc = $('attribute#objectclass input[type=text]')
|
||||
.map((key,item)=>{return $(item).val()}).toArray();
|
||||
|
@ -50,10 +50,6 @@
|
||||
<div class="alert alert-danger p-2">
|
||||
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="alert alert-danger p-2">
|
||||
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
|
||||
|
||||
<x-note/>
|
||||
@ -78,53 +74,49 @@
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-center">
|
||||
<div role="group" class="btn-group btn-group-sm nav pb-3">
|
||||
<!-- It is assumed that the entry has atleast 1 template "default" -->
|
||||
@if($o->templates->count() > 1)
|
||||
@foreach($o->templates as $template => $name)
|
||||
<span data-bs-toggle="tab" href="#template-{{$template}}" @class(['btn','btn-outline-focus','active'=>$loop->index === 0])>{{ $name }}</span>
|
||||
@endforeach
|
||||
<!-- If we have templates that cover this entry -->
|
||||
@foreach($o->templates as $template)
|
||||
<span data-bs-toggle="tab" href="#template-{{ $template->name }}" @class(['btn','btn-outline-focus','active'=>$loop->index === 0])><i class="fa fa-fw pe-2 {{ $template->icon }}"></i> {{ $template->title }}</span>
|
||||
@endforeach
|
||||
@if($o->templates->count())
|
||||
<span data-bs-toggle="tab" href="#template-default" @class(['btn','btn-outline-focus','p-1','active'=>(! $o->templates->count())])>{{ __('LDAP Entry') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
@foreach($o->templates as $template => $name)
|
||||
@switch($template)
|
||||
@case('default')
|
||||
<div @class(['tab-pane','active'=>$loop->index === 0]) id="template-{{$template}}" role="tabpanel">
|
||||
<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-body">
|
||||
<div class="tab-content">
|
||||
@php($up=(session()->pull('updated') ?: collect()))
|
||||
@foreach($o->getVisibleAttributes() as $ao)
|
||||
<x-attribute-type :o="$ao" :edit="TRUE" :new="FALSE" :updated="$up->contains($ao->name_lc)"/>
|
||||
@endforeach
|
||||
|
||||
@include('fragment.dn.add_attr')
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@break
|
||||
|
||||
@default
|
||||
<div @class(['tab-pane','active'=>$loop->index === 0]) id="template-{{$template}}" role="tabpanel">
|
||||
<p>{{$name}}</p>
|
||||
</div>
|
||||
@endswitch
|
||||
@foreach($o->templates as $template)
|
||||
<div @class(['tab-pane','active'=>$loop->index === 0]) id="template-{{ $template->name }}" role="tabpanel">
|
||||
@include('fragment.template.dn',['template'=>$template])
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row d-none pt-3">
|
||||
<div class="col-12 offset-sm-2 col-sm-4 col-lg-2">
|
||||
<x-form.reset form="dn-edit"/>
|
||||
<x-form.submit action="Update" form="dn-edit"/>
|
||||
<div @class(['tab-pane','active'=>(! $o->templates->count())]) id="template-default" role="tabpanel">
|
||||
<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-body">
|
||||
<div class="tab-content">
|
||||
@php($up=(session()->pull('updated') ?: collect()))
|
||||
@foreach($o->getVisibleAttributes() as $ao)
|
||||
<x-attribute-type :o="$ao" :edit="true" :new="false" :template="null" :updated="$up->contains($ao->name)"/>
|
||||
@endforeach
|
||||
|
||||
@include('fragment.dn.add_attr')
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="row d-none pt-3">
|
||||
<div class="col-11 text-end">
|
||||
<x-form.reset form="dn-edit"/>
|
||||
<x-form.submit :action="__('Update')" form="dn-edit"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -132,7 +124,7 @@
|
||||
<!-- Internal Attributes -->
|
||||
<div class="tab-pane mt-3" id="internal" role="tabpanel">
|
||||
@foreach($o->getInternalAttributes() as $ao)
|
||||
<x-attribute-type :o="$ao" :edit="FALSE" :new="FALSE" :updated="FALSE"/>
|
||||
<x-attribute-type :o="$ao" :edit="false" :new="false" :template="$template ?? null" :updated="false"/>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@ -166,7 +158,6 @@
|
||||
|
||||
// Find all input items and turn off readonly
|
||||
$('input.form-control').each(function() {
|
||||
// Except for objectClass - @todo show an "X" instead
|
||||
if ($(this)[0].name.match(/^objectclass/))
|
||||
return;
|
||||
|
||||
@ -291,27 +282,30 @@
|
||||
})
|
||||
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....');
|
||||
},
|
||||
})
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('No action for button:'+$(item.relatedTarget).attr('id'));
|
||||
switch ($(item.relatedTarget).attr('name')) {
|
||||
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....');
|
||||
},
|
||||
})
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('No action for button:'+$(item.relatedTarget).attr('id'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
<div class="main-card mb-3 card">
|
||||
<form id="import-form" action="{{ url('import/process/ldif') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<input type="hidden" name="key" value="{{ Crypt::encryptString('*import|_NOP') }}">
|
||||
<input type="hidden" name="_key" value="{{ Crypt::encryptString('*import|_NOP') }}">
|
||||
|
||||
<div class="card-header">
|
||||
@lang('LDIF Import')
|
||||
@ -56,7 +56,7 @@
|
||||
|
||||
<div class="card-footer">
|
||||
<span class="ms-auto">
|
||||
<x-form.submit action="Process" form="import-form"/>
|
||||
<x-form.submit :action="__('Process')" form="import-form"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -21,7 +21,11 @@
|
||||
: $attribute !!}
|
||||
</th>
|
||||
<td>
|
||||
<x-attribute :edit="false" :o="$ao"/>
|
||||
@if($ao instanceof \App\Classes\LDAP\Attribute\Schema\OID)
|
||||
<x-attribute :edit="false" :o="$ao"/>
|
||||
@else
|
||||
{!! $ao->values_old->dot()->join('<br>') !!}
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
@ -1,16 +1,5 @@
|
||||
@extends('architect::layouts.app')
|
||||
|
||||
{{--
|
||||
@section('htmlheader_title')
|
||||
@lang('Home')
|
||||
@endsection
|
||||
|
||||
@section('page_title')
|
||||
@endsection
|
||||
@section('page_icon')
|
||||
@endsection
|
||||
--}}
|
||||
|
||||
@section('main-content')
|
||||
<x-success/>
|
||||
|
||||
@ -55,6 +44,12 @@
|
||||
<li class="ps-0 p-1">
|
||||
<i class="fas fa-fw fa-sitemap me-2"></i> Hierarchical Tree View
|
||||
</li>
|
||||
<li class="ps-0 p-1">
|
||||
<i class="fas fa-fw fa-clone me-2"></i> Creation and Modification Templates
|
||||
</li>
|
||||
<li class="ps-0 p-1">
|
||||
<i class="fas fa-fw fa-magnifying-glass-chart me-2"></i> Data Rich Attribute Values
|
||||
</li>
|
||||
<li class="ps-0 p-1">
|
||||
<i class="fas fa-fw fa-language me-2"></i> Multi-language Support
|
||||
</li>
|
||||
@ -78,8 +73,6 @@
|
||||
|
||||
@section('page-scripts')
|
||||
<script type="text/javascript">
|
||||
var basedn = {!! $bases->toJson() !!};
|
||||
|
||||
var subpage = window.location.hash;
|
||||
|
||||
$(document).ready(function() {
|
||||
|
@ -64,7 +64,7 @@
|
||||
<div class="row p-3">
|
||||
<div class="col-12 offset-sm-2 col-sm-4 col-lg-2 mx-auto">
|
||||
<x-form.cancel/>
|
||||
<x-form.submit action="Update" form="dn-update"/>
|
||||
<x-form.submit :action="__('Update')" form="dn-update"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
18
templates/dns_domain.json
Normal file
18
templates/dns_domain.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"title": "Generic: DNS Entry",
|
||||
"description": "New DNS Entry",
|
||||
"enabled": true,
|
||||
"icon": "fa-globe",
|
||||
"rdn": "dc",
|
||||
|
||||
"objectclasses": [
|
||||
"dnsDomain"
|
||||
],
|
||||
|
||||
"attributes": {
|
||||
"dc": {
|
||||
"display": "Domain Component",
|
||||
"order": 1
|
||||
}
|
||||
}
|
||||
}
|
42
templates/example.json
Normal file
42
templates/example.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"title": "Example entry", // Title shown when selecting tempaltes
|
||||
"description": "This is the description", // Unused, only for documenting
|
||||
"enabled": false, // Whether template is enabled or not
|
||||
"icon": "fa-star-of-life", // Icon shown when rendering an existing entry that identifies as this template
|
||||
"rdn": "o", // @todo not implemented
|
||||
"regexp": "/^$/", // Regular expression that restricts where this template cna be used
|
||||
|
||||
"objectclasses": [ // Objectclasses that entries will have if they use this template
|
||||
"organization"
|
||||
],
|
||||
|
||||
"attributes": { // Attribute configuration
|
||||
"attribute1": { // LDAP attribute name
|
||||
"display": "Attribute 1", // Displayed when accepting input for this value
|
||||
"hint": "This is an example", // @todo not implemented
|
||||
"type": null, // Default is NULL, so use normal Attribute rendering type
|
||||
"order": 1 // Order to show attributes
|
||||
},
|
||||
"attribute2": {
|
||||
"display": "Attribute 2",
|
||||
"hint": "This is an example",
|
||||
"type": "input", // Force attribute to use template input
|
||||
"order": 2
|
||||
},
|
||||
"attribute3": {
|
||||
"display": "Attribute 3",
|
||||
"type": "select", // Force attribute to use template select
|
||||
"options": { // Select options
|
||||
"/bin/bash": "Bash",
|
||||
"/bin/csh": "C Shell",
|
||||
"/bin/dash": "Dash",
|
||||
"/bin/sh": "Shell",
|
||||
"/bin/tsh": "Turbo C Shell",
|
||||
"/bin/zsh": "ZSH",
|
||||
"/bin/false": "False",
|
||||
"/usr/sbin/nologin": "No Login"
|
||||
},
|
||||
"order": 8
|
||||
}
|
||||
}
|
||||
}
|
20
templates/o.json
Normal file
20
templates/o.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"title": "Generic: Organisational",
|
||||
"description": "New Organisational",
|
||||
"enabled": true,
|
||||
"icon": "fa-building",
|
||||
"rdn": "ou",
|
||||
"regexp": "/^c=.+,?/",
|
||||
|
||||
"objectclasses": [
|
||||
"organization"
|
||||
],
|
||||
|
||||
"attributes": {
|
||||
"o": {
|
||||
"display": "Organisation",
|
||||
"hint": "This is an example",
|
||||
"order": 1
|
||||
}
|
||||
}
|
||||
}
|
20
templates/ou.json
Normal file
20
templates/ou.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"title": "Generic: Organisational Unit",
|
||||
"description": "New Organisational Unit",
|
||||
"enabled": true,
|
||||
"icon": "fa-layer-group",
|
||||
"rdn": "ou",
|
||||
"regexp": "/^o=.+,?/",
|
||||
|
||||
"objectclasses": [
|
||||
"organizationalUnit"
|
||||
],
|
||||
|
||||
"attributes": {
|
||||
"ou": {
|
||||
"display": "Organisational Unit",
|
||||
"hint": "This is an example",
|
||||
"order": 1
|
||||
}
|
||||
}
|
||||
}
|
81
templates/user_account.json
Normal file
81
templates/user_account.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
"title": "Generic: User Account",
|
||||
"description": "New User Account",
|
||||
"enabled": true,
|
||||
"icon": "fa-user",
|
||||
"rdn": "cn",
|
||||
"regexp": "/^ou=.+,?/",
|
||||
|
||||
"objectclasses": [
|
||||
"inetOrgPerson",
|
||||
"posixAccount"
|
||||
],
|
||||
|
||||
"attributes": {
|
||||
"givenName": {
|
||||
"display": "First Name",
|
||||
"onchange": [
|
||||
"=autoFill(cn;%givenName% %sn/U%)",
|
||||
"=autoFill(uid;%givenName|0-1/l%%sn/l%)"
|
||||
],
|
||||
"order": 1
|
||||
},
|
||||
"sn": {
|
||||
"display": "Last Name",
|
||||
"onchange": [
|
||||
"=autoFill(cn;%givenName% %sn/U%)",
|
||||
"=autoFill(uid;%givenName|0-1/l%%sn/l%)"
|
||||
],
|
||||
"order": 2
|
||||
},
|
||||
"cn": {
|
||||
"display": "Common Name",
|
||||
"readonly": true,
|
||||
"order": 3
|
||||
},
|
||||
"uid": {
|
||||
"display": "User ID",
|
||||
"onchange": [
|
||||
"=autoFill(homeDirectory;/home/users/%uid%)"
|
||||
],
|
||||
"order": 4
|
||||
},
|
||||
"userPassword": {
|
||||
"display": "Password",
|
||||
"order": 5
|
||||
},
|
||||
"uidNumber": {
|
||||
"display": "UID Number",
|
||||
"readonly": true,
|
||||
"value": "=getNextNumber(/;uidNumber)",
|
||||
"order": 6
|
||||
},
|
||||
"gidNumber": {
|
||||
"display": "GID Number",
|
||||
"onchange": [
|
||||
"=autoFill(homeDirectory;/home/users/%gidNumber|0-0/T%/%uid|3-%)"
|
||||
],
|
||||
"value": "=getNextNumber(/;gidNumber)",
|
||||
"order": 7
|
||||
},
|
||||
"homeDirectory": {
|
||||
"display": "Home Directory",
|
||||
"order": 8
|
||||
},
|
||||
"loginShell": {
|
||||
"display": "Login Shell",
|
||||
"type": "select",
|
||||
"options": {
|
||||
"/bin/bash": "Bash",
|
||||
"/bin/csh": "C Shell",
|
||||
"/bin/dash": "Dash",
|
||||
"/bin/sh": "Shell",
|
||||
"/bin/tsh": "Turbo C Shell",
|
||||
"/bin/zsh": "ZSH",
|
||||
"/bin/false": "False",
|
||||
"/usr/sbin/nologin": "No Login"
|
||||
},
|
||||
"order": 9
|
||||
}
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ class ImportTest extends TestCase
|
||||
->from('/import')
|
||||
->post('/import/process/ldif',[
|
||||
'_token' => csrf_token(),
|
||||
'key'=>Crypt::encryptString('*import|_NOP'),
|
||||
'_key'=>Crypt::encryptString('*import|_NOP'),
|
||||
'file' => $file,
|
||||
]);
|
||||
|
||||
|
@ -99,10 +99,7 @@ jpegphoto:: /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkS
|
||||
Q05bKyt9PtEtrWPZEuSBkkkk5JJPJJJJJPJJoopOc5xu43SZGyMWYLDuXRRRRTU9f//Z
|
||||
l: Springfield
|
||||
mail: bart.simpson@simpsons.example.com
|
||||
mailHost: simpsons.host
|
||||
mailRoutingAddress: bsimpson@simpsons.example.com
|
||||
o: The Simpsons
|
||||
objectclass: inetLocalMailRecipient
|
||||
objectclass: inetOrgPerson
|
||||
objectclass: posixAccount
|
||||
objectclass: top
|
||||
|
4
tests/server/openldap/schema/modify/00-ssslv.ldif
Normal file
4
tests/server/openldap/schema/modify/00-ssslv.ldif
Normal file
@ -0,0 +1,4 @@
|
||||
dn: cn=z-module{0},cn=config
|
||||
changetype: modify
|
||||
add: olcModuleLoad
|
||||
olcModuleLoad: sssvlv
|
@ -1,4 +1,4 @@
|
||||
dn: olcDatabase={4}mdb,cn=config
|
||||
changetype: modify
|
||||
add: olcSizeLimit
|
||||
olcSizeLimit: 2000
|
||||
olcSizeLimit: 5
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user