Compare commits
12 Commits
aa726db11a
...
0a268fb653
Author | SHA1 | Date | |
---|---|---|---|
0a268fb653 | |||
6954b09089 | |||
a336e58b7a | |||
53880121b6 | |||
ea46cf36d0 | |||
36f8f57b77 | |||
3604f1498c | |||
808934ebfe | |||
21a690c6dd | |||
0083e9158b | |||
f4cc559931 | |||
3de46ac28e |
@ -15,4 +15,4 @@ LDAP_HOST=
|
|||||||
LDAP_BASE_DN=
|
LDAP_BASE_DN=
|
||||||
LDAP_USERNAME=
|
LDAP_USERNAME=
|
||||||
LDAP_PASSWORD=
|
LDAP_PASSWORD=
|
||||||
LDAP_CACHE=true
|
LDAP_CACHE=false
|
||||||
|
@ -61,7 +61,7 @@ Support is known for these LDAP servers:
|
|||||||
- [X] OpenLDAP
|
- [X] OpenLDAP
|
||||||
- [X] OpenDJ
|
- [X] OpenDJ
|
||||||
- [ ] Microsoft Active Directory
|
- [ ] Microsoft Active Directory
|
||||||
- [ ] 389 Directory Server
|
- [X] 389 Directory Server
|
||||||
|
|
||||||
If there is an LDAP server that you have that you would like to have supported, please open an issue to request it.
|
If there is an LDAP server that you have that you would like to have supported, please open an issue to request it.
|
||||||
You might need to provide access, provide a copy or instructions to get an environment for testing. If you have enabled
|
You might need to provide access, provide a copy or instructions to get an environment for testing. If you have enabled
|
||||||
|
@ -311,7 +311,7 @@ class Attribute implements \Countable, \ArrayAccess
|
|||||||
*/
|
*/
|
||||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
{
|
{
|
||||||
$view = match ($this->schema->syntax_oid) {
|
$view = match ($this->schema?->syntax_oid) {
|
||||||
self::SYNTAX_CERTIFICATE => view('components.syntax.certificate'),
|
self::SYNTAX_CERTIFICATE => view('components.syntax.certificate'),
|
||||||
self::SYNTAX_CERTIFICATE_LIST => view('components.syntax.certificatelist'),
|
self::SYNTAX_CERTIFICATE_LIST => view('components.syntax.certificatelist'),
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ abstract class Base {
|
|||||||
protected string $name = '';
|
protected string $name = '';
|
||||||
|
|
||||||
// The OID of this schema item.
|
// The OID of this schema item.
|
||||||
protected string $oid;
|
protected string $oid = '';
|
||||||
|
|
||||||
# The description of this schema item.
|
# The description of this schema item.
|
||||||
protected string $description = '';
|
protected string $description = '';
|
||||||
|
@ -43,7 +43,7 @@ final class ObjectClass extends Base
|
|||||||
*
|
*
|
||||||
* @param string $line Schema Line
|
* @param string $line Schema Line
|
||||||
* @param Server $server
|
* @param Server $server
|
||||||
* @todo Change $server to $connection, no need to store the server object here
|
* @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)
|
public function __construct(string $line,Server $server)
|
||||||
{
|
{
|
||||||
|
@ -8,11 +8,11 @@ use Illuminate\Support\Arr;
|
|||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Config;
|
use Illuminate\Support\Facades\Config;
|
||||||
use Illuminate\Support\Facades\Cookie;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
use LdapRecord\LdapRecordException;
|
use LdapRecord\LdapRecordException;
|
||||||
use LdapRecord\Models\Model;
|
use LdapRecord\Models\Model;
|
||||||
|
use LdapRecord\Query\Builder;
|
||||||
use LdapRecord\Query\Collection as LDAPCollection;
|
use LdapRecord\Query\Collection as LDAPCollection;
|
||||||
use LdapRecord\Query\ObjectNotFoundException;
|
use LdapRecord\Query\ObjectNotFoundException;
|
||||||
|
|
||||||
@ -22,8 +22,7 @@ use App\Ldap\Entry;
|
|||||||
|
|
||||||
final class Server
|
final class Server
|
||||||
{
|
{
|
||||||
// Connection information used for these object and children
|
private const LOGKEY = 'SVR';
|
||||||
private ?string $connection;
|
|
||||||
|
|
||||||
// This servers schema objectclasses
|
// This servers schema objectclasses
|
||||||
private Collection $attributetypes;
|
private Collection $attributetypes;
|
||||||
@ -37,22 +36,24 @@ final class Server
|
|||||||
public const OC_ABSTRACT = 0x02;
|
public const OC_ABSTRACT = 0x02;
|
||||||
public const OC_AUXILIARY = 0x03;
|
public const OC_AUXILIARY = 0x03;
|
||||||
|
|
||||||
public function __construct(?string $connection=NULL)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->connection = $connection;
|
$this->attributetypes = collect();
|
||||||
|
$this->ldapsyntaxes = collect();
|
||||||
|
$this->matchingrules = collect();
|
||||||
|
$this->objectclasses = collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __get(string $key): mixed
|
public function __get(string $key): mixed
|
||||||
{
|
{
|
||||||
return match($key) {
|
return match($key) {
|
||||||
'attributetypes' => $this->attributetypes,
|
'attributetypes' => $this->attributetypes,
|
||||||
'connection' => $this->connection,
|
|
||||||
'ldapsyntaxes' => $this->ldapsyntaxes,
|
'ldapsyntaxes' => $this->ldapsyntaxes,
|
||||||
'matchingrules' => $this->matchingrules,
|
'matchingrules' => $this->matchingrules,
|
||||||
'objectclasses' => $this->objectclasses,
|
'objectclasses' => $this->objectclasses,
|
||||||
'config' => config('ldap.connections.'.config('ldap.default')),
|
'config' => config(sprintf('ldap.connections.%s',config('ldap.default'))),
|
||||||
'name' => Arr::get($this->config,'name',__('No Server Name Yet')),
|
'name' => Arr::get($this->config,'name',__('No Server Name Yet')),
|
||||||
default => throw new Exception('Unknown key:' . $key),
|
default => throw new Exception('Unknown key:'.$key),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,20 +63,14 @@ final class Server
|
|||||||
* Gets the root DN of the specified LDAPServer, or throws an exception if it
|
* Gets the root DN of the specified LDAPServer, or throws an exception if it
|
||||||
* can't find it.
|
* can't find it.
|
||||||
*
|
*
|
||||||
* @param string|null $connection Return a collection of baseDNs
|
|
||||||
* @param bool $objects Return a collection of Entry Models
|
* @param bool $objects Return a collection of Entry Models
|
||||||
* @return Collection
|
* @return Collection
|
||||||
* @throws ObjectNotFoundException
|
|
||||||
* @testedin GetBaseDNTest::testBaseDNExists();
|
* @testedin GetBaseDNTest::testBaseDNExists();
|
||||||
* @todo Need to allow for the scenario if the baseDN is not readable by ACLs
|
|
||||||
*/
|
*/
|
||||||
public static function baseDNs(?string $connection=NULL,bool $objects=TRUE): Collection
|
public static function baseDNs(bool $objects=FALSE): Collection
|
||||||
{
|
{
|
||||||
$cachetime = Carbon::now()
|
|
||||||
->addSeconds(Config::get('ldap.cache.time'));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$base = self::rootDSE($connection,$cachetime);
|
$rootdse = self::rootDSE();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LDAP Error Codes:
|
* LDAP Error Codes:
|
||||||
@ -173,16 +168,6 @@ final class Server
|
|||||||
} catch (LdapRecordException $e) {
|
} catch (LdapRecordException $e) {
|
||||||
switch ($e->getDetailedError()?->getErrorCode()) {
|
switch ($e->getDetailedError()?->getErrorCode()) {
|
||||||
case 49:
|
case 49:
|
||||||
// Since we failed authentication, we should delete our auth cookie
|
|
||||||
if (Cookie::has('password_encrypt')) {
|
|
||||||
Log::alert('Clearing user credentials and logging out');
|
|
||||||
|
|
||||||
Cookie::queue(Cookie::forget('password_encrypt'));
|
|
||||||
Cookie::queue(Cookie::forget('username_encrypt'));
|
|
||||||
|
|
||||||
Session::invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
abort(401,$e->getDetailedError()->getErrorMessage());
|
abort(401,$e->getDetailedError()->getErrorMessage());
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -191,57 +176,100 @@ final class Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! $objects)
|
if (! $objects)
|
||||||
return collect($base->namingcontexts);
|
return collect($rootdse->namingcontexts);
|
||||||
|
|
||||||
/**
|
return Cache::remember('basedns'.Session::id(),config('ldap.cache.time'),function() use ($rootdse) {
|
||||||
* @note While we are caching our baseDNs, it seems if we have more than 1,
|
$result = collect();
|
||||||
* our caching doesnt generate a hit on a subsequent call to this function (before the cache expires).
|
|
||||||
* IE: If we have 5 baseDNs, it takes 5 calls to this function to case them all.
|
|
||||||
* @todo Possibly a bug wtih ldaprecord, so need to investigate
|
|
||||||
*/
|
|
||||||
$result = collect();
|
|
||||||
foreach ($base->namingcontexts as $dn)
|
|
||||||
$result->push((new Entry)->cache($cachetime)->findOrFail($dn));
|
|
||||||
|
|
||||||
return $result;
|
// @note: Incase our rootDSE didnt return a namingcontext, we'll have no base DNs
|
||||||
|
foreach (($rootdse->namingcontexts ?: []) as $dn)
|
||||||
|
$result->push(self::get($dn)->read()->find($dn));
|
||||||
|
|
||||||
|
return $result->filter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Work out if we should flush the cache when retrieving an entry
|
||||||
|
*
|
||||||
|
* @param string $dn
|
||||||
|
* @return bool
|
||||||
|
* @note: We dont need to flush the cache for internal LDAP attributes, as we dont change them
|
||||||
|
*/
|
||||||
|
private static function cacheflush(string $dn): bool
|
||||||
|
{
|
||||||
|
$cache = (! config('ldap.cache.enabled'))
|
||||||
|
|| match (strtolower($dn)) {
|
||||||
|
'','cn=schema','cn=subschema' => FALSE,
|
||||||
|
default => TRUE,
|
||||||
|
};
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:%s - %s',self::LOGKEY,$cache ? 'Caching' : 'Not Cached',$dn));
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return our cache time as per the configuration
|
||||||
|
*
|
||||||
|
* @return Carbon
|
||||||
|
*/
|
||||||
|
private static function cachetime(): Carbon
|
||||||
|
{
|
||||||
|
return Carbon::now()
|
||||||
|
->addSeconds(Config::get('ldap.cache.time'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic Builder method to setup our queries consistently - mainly to ensure we cache results
|
||||||
|
*
|
||||||
|
* @param string $dn
|
||||||
|
* @param array $attrs
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
private static function get(string $dn,array $attrs=['*','+']): Builder
|
||||||
|
{
|
||||||
|
return Entry::query()
|
||||||
|
->setDN($dn)
|
||||||
|
->cache(
|
||||||
|
until: self::cachetime(),
|
||||||
|
flush: self::cacheflush($dn))
|
||||||
|
->select($attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the rootDSE for the server, that gives us server information
|
* Obtain the rootDSE for the server, that gives us server information
|
||||||
*
|
*
|
||||||
* @param string|null $connection
|
* @return Model
|
||||||
* @param Carbon|null $cachetime
|
|
||||||
* @return Entry|null
|
|
||||||
* @throws ObjectNotFoundException
|
* @throws ObjectNotFoundException
|
||||||
* @testedin TranslateOidTest::testRootDSE();
|
* @testedin TranslateOidTest::testRootDSE();
|
||||||
|
* @note While we are using a static variable for in session performance, we'll also cache the result normally
|
||||||
*/
|
*/
|
||||||
public static function rootDSE(?string $connection=NULL,?Carbon $cachetime=NULL): ?Model
|
public static function rootDSE(): Model
|
||||||
{
|
{
|
||||||
$e = new Entry;
|
static $rootdse = NULL;
|
||||||
|
|
||||||
return Entry::on($connection ?? $e->getConnectionName())
|
if (is_null($rootdse))
|
||||||
->cache($cachetime)
|
$rootdse = self::get('',['+','*'])
|
||||||
->in(NULL)
|
->read()
|
||||||
->read()
|
->firstOrFail();
|
||||||
->select(['+'])
|
|
||||||
->whereHas('objectclass')
|
return $rootdse;
|
||||||
->firstOrFail();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Schema DN
|
* Get the Schema DN
|
||||||
*
|
*
|
||||||
* @param string|null $connection
|
|
||||||
* @return string
|
* @return string
|
||||||
* @throws ObjectNotFoundException
|
* @throws ObjectNotFoundException
|
||||||
*/
|
*/
|
||||||
public static function schemaDN(?string $connection=NULL): string
|
public static function schemaDN(): string
|
||||||
{
|
{
|
||||||
$cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time'));
|
return collect(self::rootDSE()->subschemasubentry)
|
||||||
|
->first();
|
||||||
return collect(self::rootDSE($connection,$cachetime)->subschemasubentry)->first();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* METHODS */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the server for a DN and return its children and if those children have children.
|
* Query the server for a DN and return its children and if those children have children.
|
||||||
*
|
*
|
||||||
@ -251,17 +279,16 @@ final class Server
|
|||||||
*/
|
*/
|
||||||
public function children(string $dn,array $attrs=['dn']): ?LDAPCollection
|
public function children(string $dn,array $attrs=['dn']): ?LDAPCollection
|
||||||
{
|
{
|
||||||
return ($x=(new Entry)
|
return $this
|
||||||
->on($this->connection)
|
->get(
|
||||||
->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
dn: $dn,
|
||||||
->select(array_merge($attrs,[
|
attrs: array_merge($attrs,[
|
||||||
'hassubordinates', // Needed for the tree to know if an entry has children
|
'hassubordinates', // Needed for the tree to know if an entry has children
|
||||||
'c' // Needed for the tree to show icons for countries
|
'c' // Needed for the tree to show icons for countries
|
||||||
]))
|
]))
|
||||||
->setDn($dn)
|
|
||||||
->list()
|
->list()
|
||||||
->orderBy('dn')
|
->orderBy('dn')
|
||||||
->get()) ? $x : NULL;
|
->get() ?: NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -269,15 +296,13 @@ final class Server
|
|||||||
*
|
*
|
||||||
* @param string $dn
|
* @param string $dn
|
||||||
* @param array $attrs
|
* @param array $attrs
|
||||||
* @return Entry|null
|
* @return Model|null
|
||||||
*/
|
*/
|
||||||
public function fetch(string $dn,array $attrs=['*','+']): ?Entry
|
public function fetch(string $dn,array $attrs=['*','+']): ?Model
|
||||||
{
|
{
|
||||||
return ($x=(new Entry)
|
return $this->get($dn,$attrs)
|
||||||
->on($this->connection)
|
->read()
|
||||||
->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
->first() ?: NULL;
|
||||||
->select($attrs)
|
|
||||||
->find($dn)) ? $x : NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -285,6 +310,7 @@ final class Server
|
|||||||
* as configured in config.php.
|
* as configured in config.php.
|
||||||
*
|
*
|
||||||
* @return boolean True if the specified attribute is configured to be force as a may attribute
|
* @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
|
||||||
*/
|
*/
|
||||||
public function isForceMay($attr_name): bool
|
public function isForceMay($attr_name): bool
|
||||||
{
|
{
|
||||||
@ -321,34 +347,11 @@ final class Server
|
|||||||
// First pass if we have already retrieved the schema item
|
// First pass if we have already retrieved the schema item
|
||||||
switch ($item) {
|
switch ($item) {
|
||||||
case 'attributetypes':
|
case 'attributetypes':
|
||||||
if (isset($this->attributetypes))
|
|
||||||
return $this->attributetypes;
|
|
||||||
else
|
|
||||||
$this->attributetypes = collect();
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ldapsyntaxes':
|
case 'ldapsyntaxes':
|
||||||
if (isset($this->ldapsyntaxes))
|
|
||||||
return $this->ldapsyntaxes;
|
|
||||||
else
|
|
||||||
$this->ldapsyntaxes = collect();
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'matchingrules':
|
case 'matchingrules':
|
||||||
if (isset($this->matchingrules))
|
|
||||||
return $this->matchingrules;
|
|
||||||
else
|
|
||||||
$this->matchingrules = collect();
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'objectclasses':
|
case 'objectclasses':
|
||||||
if (isset($this->objectclasses))
|
if ($this->{$item}->count())
|
||||||
return $this->objectclasses;
|
return $this->{$item};
|
||||||
else
|
|
||||||
$this->objectclasses = collect();
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -358,8 +361,9 @@ final class Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to get the schema DN from the specified entry.
|
// Try to get the schema DN from the specified entry.
|
||||||
$schema_dn = $this->schemaDN($this->connection);
|
$schema_dn = $this->schemaDN();
|
||||||
$schema = $this->fetch($schema_dn);
|
// @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 our schema's null, we didnt find it.
|
||||||
if (! $schema)
|
if (! $schema)
|
||||||
@ -367,7 +371,7 @@ final class Server
|
|||||||
|
|
||||||
switch ($item) {
|
switch ($item) {
|
||||||
case 'attributetypes':
|
case 'attributetypes':
|
||||||
Log::debug('Attribute Types');
|
Log::debug(sprintf('%s:Attribute Types',self::LOGKEY));
|
||||||
// build the array of attribueTypes
|
// build the array of attribueTypes
|
||||||
//$syntaxes = $this->SchemaSyntaxes($dn);
|
//$syntaxes = $this->SchemaSyntaxes($dn);
|
||||||
|
|
||||||
@ -385,7 +389,7 @@ final class Server
|
|||||||
* with its name set to the alias name, and all other data copied.*/
|
* with its name set to the alias name, and all other data copied.*/
|
||||||
|
|
||||||
if ($o->aliases->count()) {
|
if ($o->aliases->count()) {
|
||||||
Log::debug(sprintf('\ Attribute [%s] has the following aliases [%s]',$o->name,$o->aliases->join(',')));
|
Log::debug(sprintf('%s:\ Attribute [%s] has the following aliases [%s]',self::LOGKEY,$o->name,$o->aliases->join(',')));
|
||||||
|
|
||||||
foreach ($o->aliases as $alias) {
|
foreach ($o->aliases as $alias) {
|
||||||
$new_attr = clone $o;
|
$new_attr = clone $o;
|
||||||
@ -445,7 +449,7 @@ final class Server
|
|||||||
return $this->attributetypes;
|
return $this->attributetypes;
|
||||||
|
|
||||||
case 'ldapsyntaxes':
|
case 'ldapsyntaxes':
|
||||||
Log::debug('LDAP Syntaxes');
|
Log::debug(sprintf('%s:LDAP Syntaxes',self::LOGKEY));
|
||||||
|
|
||||||
foreach ($schema->{$item} as $line) {
|
foreach ($schema->{$item} as $line) {
|
||||||
if (is_null($line) || ! strlen($line))
|
if (is_null($line) || ! strlen($line))
|
||||||
@ -458,7 +462,7 @@ final class Server
|
|||||||
return $this->ldapsyntaxes;
|
return $this->ldapsyntaxes;
|
||||||
|
|
||||||
case 'matchingrules':
|
case 'matchingrules':
|
||||||
Log::debug('Matching Rules');
|
Log::debug(sprintf('%s:Matching Rules',self::LOGKEY));
|
||||||
$this->matchingruleuse = collect();
|
$this->matchingruleuse = collect();
|
||||||
|
|
||||||
foreach ($schema->{$item} as $line) {
|
foreach ($schema->{$item} as $line) {
|
||||||
@ -499,7 +503,7 @@ final class Server
|
|||||||
return $this->matchingrules;
|
return $this->matchingrules;
|
||||||
|
|
||||||
case 'objectclasses':
|
case 'objectclasses':
|
||||||
Log::debug('Object Classes');
|
Log::debug(sprintf('%s:Object Classes',self::LOGKEY));
|
||||||
|
|
||||||
foreach ($schema->{$item} as $line) {
|
foreach ($schema->{$item} as $line) {
|
||||||
if (is_null($line) || ! strlen($line))
|
if (is_null($line) || ! strlen($line))
|
||||||
|
@ -10,17 +10,18 @@ use Illuminate\Support\Collection;
|
|||||||
|
|
||||||
use App\Classes\LDAP\Server;
|
use App\Classes\LDAP\Server;
|
||||||
|
|
||||||
class APIController extends Controller
|
class AjaxController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Get the LDAP server BASE DNs
|
* Get the LDAP server BASE DNs
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return Collection
|
||||||
* @throws \LdapRecord\Query\ObjectNotFoundException
|
* @throws \LdapRecord\Query\ObjectNotFoundException
|
||||||
|
* @todo This should be consolidated with HomeController
|
||||||
*/
|
*/
|
||||||
public function bases(): Collection
|
public function bases(): Collection
|
||||||
{
|
{
|
||||||
$base = Server::baseDNs() ?: collect();
|
$base = Server::baseDNs(TRUE) ?: collect();
|
||||||
|
|
||||||
return $base
|
return $base
|
||||||
->transform(fn($item)=>
|
->transform(fn($item)=>
|
@ -5,41 +5,43 @@ namespace App\Http\Controllers\Auth;
|
|||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cookie;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
class LoginController extends Controller
|
class LoginController extends Controller
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Login Controller
|
| Login Controller
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| This controller handles authenticating users for the application and
|
| This controller handles authenticating users for the application and
|
||||||
| redirecting them to your home screen. The controller uses a trait
|
| redirecting them to your home screen. The controller uses a trait
|
||||||
| to conveniently provide its functionality to your applications.
|
| to conveniently provide its functionality to your applications.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use AuthenticatesUsers;
|
use AuthenticatesUsers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where to redirect users after login.
|
* Where to redirect users after login.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = '/';
|
protected $redirectTo = '/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* Create a new controller instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('guest')->except('logout');
|
$this->middleware('guest')
|
||||||
}
|
->except('logout');
|
||||||
|
}
|
||||||
|
|
||||||
protected function credentials(Request $request): array
|
protected function credentials(Request $request): array
|
||||||
{
|
{
|
||||||
@ -58,17 +60,14 @@ class LoginController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function logout(Request $request)
|
public function logout(Request $request)
|
||||||
{
|
{
|
||||||
// Delete our LDAP authentication cookies
|
$user = Auth::user();
|
||||||
Cookie::queue(Cookie::forget('username_encrypt'));
|
|
||||||
Cookie::queue(Cookie::forget('password_encrypt'));
|
|
||||||
|
|
||||||
$this->guard()->logout();
|
$this->guard()->logout();
|
||||||
|
|
||||||
$request->session()->invalidate();
|
$request->session()->invalidate();
|
||||||
|
|
||||||
$request->session()->regenerateToken();
|
$request->session()->regenerateToken();
|
||||||
|
|
||||||
if ($response = $this->loggedOut($request)) {
|
if ($response = $this->loggedOut($request)) {
|
||||||
|
Log::info(sprintf('Logged out [%s]',$user->dn));
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,4 +99,4 @@ class LoginController extends Controller
|
|||||||
{
|
{
|
||||||
return login_attr_name();
|
return login_attr_name();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -28,7 +28,7 @@ class HomeController extends Controller
|
|||||||
{
|
{
|
||||||
private function bases(): Collection
|
private function bases(): Collection
|
||||||
{
|
{
|
||||||
$base = Server::baseDNs() ?: collect();
|
$base = Server::baseDNs(TRUE) ?: collect();
|
||||||
|
|
||||||
return $base->transform(function($item) {
|
return $base->transform(function($item) {
|
||||||
return [
|
return [
|
||||||
@ -45,7 +45,7 @@ class HomeController extends Controller
|
|||||||
* Create a new object in the LDAP server
|
* Create a new object in the LDAP server
|
||||||
*
|
*
|
||||||
* @param EntryAddRequest $request
|
* @param EntryAddRequest $request
|
||||||
* @return View
|
* @return \Illuminate\View\View
|
||||||
* @throws InvalidUsage
|
* @throws InvalidUsage
|
||||||
*/
|
*/
|
||||||
public function entry_add(EntryAddRequest $request): \Illuminate\View\View
|
public function entry_add(EntryAddRequest $request): \Illuminate\View\View
|
||||||
@ -189,8 +189,7 @@ class HomeController extends Controller
|
|||||||
{
|
{
|
||||||
$dn = Crypt::decryptString($id);
|
$dn = Crypt::decryptString($id);
|
||||||
|
|
||||||
$result = (new Entry)
|
$result = Entry::query()
|
||||||
->query()
|
|
||||||
->setDn($dn)
|
->setDn($dn)
|
||||||
->recursive()
|
->recursive()
|
||||||
->get();
|
->get();
|
||||||
|
@ -4,7 +4,7 @@ namespace App\Http\Middleware;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cookie;
|
use Illuminate\Support\Facades\Session;
|
||||||
|
|
||||||
class AllowAnonymous
|
class AllowAnonymous
|
||||||
{
|
{
|
||||||
@ -17,7 +17,9 @@ class AllowAnonymous
|
|||||||
*/
|
*/
|
||||||
public function handle(Request $request,Closure $next): mixed
|
public function handle(Request $request,Closure $next): mixed
|
||||||
{
|
{
|
||||||
if (((! Cookie::has('username_encrypt')) || (! Cookie::has('password_encrypt'))) && (! config('pla.allow_guest',FALSE)))
|
if ((! config('pla.allow_guest',FALSE))
|
||||||
|
&& ($request->path() !== 'login')
|
||||||
|
&& ((! Session::has('username_encrypt')) || (! Session::has('password_encrypt'))))
|
||||||
return redirect()
|
return redirect()
|
||||||
->to('/login');
|
->to('/login');
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ use Illuminate\Http\Request;
|
|||||||
use Illuminate\Support\Facades\Config;
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
use App\Classes\LDAP\Server;
|
use App\Classes\LDAP\Server;
|
||||||
use App\Ldap\User;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This sets up our application session with any required values, ultimately for cache optimisation reasons
|
* This sets up our application session with any required values, ultimately for cache optimisation reasons
|
||||||
@ -25,9 +24,6 @@ class ApplicationSession
|
|||||||
{
|
{
|
||||||
Config::set('server',new Server);
|
Config::set('server',new Server);
|
||||||
|
|
||||||
view()->share('server', Config::get('server'));
|
|
||||||
view()->share('user', auth()->user() ?: new User);
|
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,11 +5,12 @@ namespace App\Http\Middleware;
|
|||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Config;
|
use Illuminate\Support\Facades\Config;
|
||||||
use Illuminate\Support\Facades\Cookie;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
use LdapRecord\Container;
|
use LdapRecord\Container;
|
||||||
|
|
||||||
use App\Ldap\Connection;
|
use App\Ldap\Guard;
|
||||||
|
|
||||||
class SwapinAuthUser
|
class SwapinAuthUser
|
||||||
{
|
{
|
||||||
@ -28,25 +29,19 @@ class SwapinAuthUser
|
|||||||
if (! array_key_exists($key,config('ldap.connections')))
|
if (! array_key_exists($key,config('ldap.connections')))
|
||||||
abort(599,sprintf('LDAP default server [%s] configuration doesnt exist?',$key));
|
abort(599,sprintf('LDAP default server [%s] configuration doesnt exist?',$key));
|
||||||
|
|
||||||
/*
|
|
||||||
// Rebuild our connection with the authenticated user.
|
|
||||||
if (Session::has('username_encrypt') && Session::has('password_encrypt')) {
|
if (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.'.username',Crypt::decryptString(Session::get('username_encrypt')));
|
||||||
Config::set('ldap.connections.'.$key.'.password',Crypt::decryptString(Session::get('password_encrypt')));
|
Config::set('ldap.connections.'.$key.'.password',Crypt::decryptString(Session::get('password_encrypt')));
|
||||||
|
|
||||||
} else
|
Log::debug('Swapping out configured LDAP credentials with the user\'s session.',['key'=>$key]);
|
||||||
*/
|
|
||||||
|
|
||||||
// @todo it seems sometimes we have cookies that show the logged in user, but Auth::user() has expired?
|
|
||||||
if (Cookie::has('username_encrypt') && Cookie::has('password_encrypt')) {
|
|
||||||
Config::set('ldap.connections.'.$key.'.username',Cookie::get('username_encrypt'));
|
|
||||||
Config::set('ldap.connections.'.$key.'.password',Cookie::get('password_encrypt'));
|
|
||||||
|
|
||||||
Log::debug('Swapping out configured LDAP credentials with the user\'s cookie.',['key'=>$key,'user'=>Cookie::get('username_encrypt')]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to override our Connection object so that we can store and retrieve the logged in user and swap out the credentials to use them.
|
// We need to override our Connection object so that we can store and retrieve the logged in user and swap out the credentials to use them.
|
||||||
Container::getInstance()->addConnection(new Connection(config('ldap.connections.'.$key)),$key);
|
$c = Container::getInstance()
|
||||||
|
->getConnection($key);
|
||||||
|
|
||||||
|
$c->setConfiguration(config('ldap.connections.'.$key));
|
||||||
|
$c->setGuardResolver(fn()=>new Guard($c->getLdapConnection(),$c->getConfiguration()));
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
30
app/Http/Middleware/ViewVariables.php
Normal file
30
app/Http/Middleware/ViewVariables.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Ldap\User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sets up our application session with any required values, ultimately for cache optimisation reasons
|
||||||
|
*/
|
||||||
|
class ViewVariables
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle(Request $request,Closure $next): mixed
|
||||||
|
{
|
||||||
|
view()->share('server',Config::get('server'));
|
||||||
|
view()->share('user',auth()->user() ?: new User);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Ldap;
|
|
||||||
|
|
||||||
use LdapRecord\Configuration\DomainConfiguration;
|
|
||||||
use LdapRecord\Connection as ConnectionBase;
|
|
||||||
use LdapRecord\LdapInterface;
|
|
||||||
|
|
||||||
class Connection extends ConnectionBase
|
|
||||||
{
|
|
||||||
|
|
||||||
public function __construct(DomainConfiguration|array $config=[],?LdapInterface $ldap=NULL)
|
|
||||||
{
|
|
||||||
parent::__construct($config,$ldap);
|
|
||||||
|
|
||||||
// We need to override this so that we use our own Guard, that stores the users credentials in the session
|
|
||||||
$this->authGuardResolver = function () {
|
|
||||||
return new Guard($this->ldap, $this->configuration);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,8 +27,6 @@ class Entry extends Model
|
|||||||
|
|
||||||
// Our Attribute objects
|
// Our Attribute objects
|
||||||
private Collection $objects;
|
private Collection $objects;
|
||||||
/* @deprecated */
|
|
||||||
private bool $noObjectAttributes = FALSE;
|
|
||||||
// For new entries, this is the container that this entry will be stored in
|
// For new entries, this is the container that this entry will be stored in
|
||||||
private string $rdnbase;
|
private string $rdnbase;
|
||||||
|
|
||||||
@ -71,8 +69,6 @@ class Entry extends Model
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the new and old values for a given key are equivalent.
|
* Determine if the new and old values for a given key are equivalent.
|
||||||
*
|
|
||||||
* @todo This function barfs on language tags, eg: key = givenname;lang-ja
|
|
||||||
*/
|
*/
|
||||||
protected function originalIsEquivalent(string $key): bool
|
protected function originalIsEquivalent(string $key): bool
|
||||||
{
|
{
|
||||||
@ -84,16 +80,6 @@ class Entry extends Model
|
|||||||
|| (! $this->getObject($attribute)->isDirty());
|
|| (! $this->getObject($attribute)->isDirty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function query(bool $noattrs=false): Builder
|
|
||||||
{
|
|
||||||
$o = new static;
|
|
||||||
|
|
||||||
if ($noattrs)
|
|
||||||
$o->noObjectAttributes();
|
|
||||||
|
|
||||||
return $o->newQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* As attribute values are updated, or new ones created, we need to mirror that
|
* As attribute values are updated, or new ones created, we need to mirror that
|
||||||
* into our $objects. This is called when we $o->key = $value
|
* into our $objects. This is called when we $o->key = $value
|
||||||
@ -539,19 +525,6 @@ class Entry extends Model
|
|||||||
return [$attribute,$tags];
|
return [$attribute,$tags];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Dont convert our $this->attributes to $this->objects when creating a new Entry::class
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
public function noObjectAttributes(): static
|
|
||||||
{
|
|
||||||
$this->noObjectAttributes = TRUE;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setRDNBase(string $bdn): void
|
public function setRDNBase(string $bdn): void
|
||||||
{
|
{
|
||||||
if ($this->exists)
|
if ($this->exists)
|
||||||
|
@ -2,26 +2,20 @@
|
|||||||
|
|
||||||
namespace App\Ldap;
|
namespace App\Ldap;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Cookie;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
// use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Log;
|
||||||
use LdapRecord\Auth\Guard as GuardBase;
|
use LdapRecord\Auth\Guard as GuardBase;
|
||||||
|
|
||||||
class Guard extends GuardBase
|
class Guard extends GuardBase
|
||||||
{
|
{
|
||||||
public function attempt(string $username, string $password, bool $stayBound = false): bool
|
public function attempt(string $username, string $password, bool $stayBound = false): bool
|
||||||
{
|
{
|
||||||
if ($result = parent::attempt($username,$password,$stayBound)) {
|
Log::info(sprintf('Attempting login for [%s] with password [%s]',$username,($password ? str_repeat('*',16) : str_repeat('?',16))));
|
||||||
/*
|
|
||||||
* We can either use our session or cookies to store this. If using session, then Http/Kernel needs to be
|
|
||||||
* updated to start a session for API calls.
|
|
||||||
// We need to store our password so that we can swap in the user in during SwapinAuthUser::class middleware
|
|
||||||
request()->session()->put('username_encrypt',Crypt::encryptString($username));
|
|
||||||
request()->session()->put('password_encrypt',Crypt::encryptString($password));
|
|
||||||
*/
|
|
||||||
|
|
||||||
// For our API calls, we store the cookie - which our cookies are already encrypted
|
if ($result = parent::attempt($username,$password,$stayBound)) {
|
||||||
Cookie::queue('username_encrypt',$username);
|
// Store user details so we can swap in auth details in SwapinAuthUser
|
||||||
Cookie::queue('password_encrypt',$password);
|
session()->put('username_encrypt',Crypt::encryptString($username));
|
||||||
|
session()->put('password_encrypt',Crypt::encryptString($password));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
@ -31,7 +31,7 @@ class LdapUserRepository extends LdapUserRepositoryBase
|
|||||||
return $this->query()->find($credentials['dn']);
|
return $this->query()->find($credentials['dn']);
|
||||||
|
|
||||||
// Look for a user using all our baseDNs
|
// Look for a user using all our baseDNs
|
||||||
foreach (Server::baseDNs() as $base) {
|
foreach (Server::baseDNs(FALSE) as $base) {
|
||||||
$query = $this->query()->setBaseDn($base);
|
$query = $this->query()->setBaseDn($base);
|
||||||
|
|
||||||
foreach ($credentials as $key => $value) {
|
foreach ($credentials as $key => $value) {
|
||||||
@ -67,7 +67,7 @@ class LdapUserRepository extends LdapUserRepositoryBase
|
|||||||
public function findByGuid($guid): ?Model
|
public function findByGuid($guid): ?Model
|
||||||
{
|
{
|
||||||
// Look for a user using all our baseDNs
|
// Look for a user using all our baseDNs
|
||||||
foreach (Server::baseDNs() as $base) {
|
foreach (Server::baseDNs(FALSE) as $base) {
|
||||||
$user = $this->query()->setBaseDn($base)->findByGuid($guid);
|
$user = $this->query()->setBaseDn($base)->findByGuid($guid);
|
||||||
|
|
||||||
if ($user)
|
if ($user)
|
||||||
|
@ -1,32 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Foundation\Configuration\Exceptions;
|
use Illuminate\Foundation\Configuration\Exceptions;
|
||||||
use Illuminate\Foundation\Configuration\Middleware;
|
use Illuminate\Foundation\Configuration\Middleware;
|
||||||
|
|
||||||
use App\Http\Middleware\{AllowAnonymous,ApplicationSession,CheckUpdate,SwapinAuthUser};
|
use App\Http\Middleware\{AllowAnonymous,ApplicationSession,CheckUpdate,SwapinAuthUser,ViewVariables};
|
||||||
|
|
||||||
return Application::configure(basePath: dirname(__DIR__))
|
return Application::configure(basePath: dirname(__DIR__))
|
||||||
->withRouting(
|
->withRouting(
|
||||||
web: __DIR__.'/../routes/web.php',
|
web: __DIR__.'/../routes/web.php',
|
||||||
api: __DIR__.'/../routes/api.php',
|
|
||||||
commands: __DIR__.'/../routes/console.php',
|
commands: __DIR__.'/../routes/console.php',
|
||||||
health: '/up',
|
health: '/up',
|
||||||
)
|
)
|
||||||
->withMiddleware(function (Middleware $middleware) {
|
->withMiddleware(function (Middleware $middleware) {
|
||||||
$middleware->appendToGroup('web', [
|
$middleware->appendToGroup(
|
||||||
SwapinAuthUser::class,
|
group: 'web',
|
||||||
ApplicationSession::class,
|
middleware: [
|
||||||
CheckUpdate::class,
|
AllowAnonymous::class,
|
||||||
]);
|
ApplicationSession::class,
|
||||||
|
SwapinAuthUser::class,
|
||||||
$middleware->prependToGroup('api', [
|
ViewVariables::class,
|
||||||
EncryptCookies::class,
|
CheckUpdate::class,
|
||||||
SwapinAuthUser::class,
|
]);
|
||||||
ApplicationSession::class,
|
|
||||||
AllowAnonymous::class,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$middleware->trustProxies(at: [
|
$middleware->trustProxies(at: [
|
||||||
'10.0.0.0/8',
|
'10.0.0.0/8',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
App\Providers\AppServiceProvider::class,
|
App\Providers\AppServiceProvider::class,
|
||||||
];
|
];
|
||||||
|
@ -68,7 +68,7 @@ return [
|
|||||||
'daily' => [
|
'daily' => [
|
||||||
'driver' => 'daily',
|
'driver' => 'daily',
|
||||||
'path' => storage_path('logs/laravel.log'),
|
'path' => storage_path('logs/laravel.log'),
|
||||||
'level' => env('LOG_LEVEL', 'debug'),
|
'level' => env('LOG_LEVEL', 'info'),
|
||||||
'days' => env('LOG_DAILY_DAYS', 14),
|
'days' => env('LOG_DAILY_DAYS', 14),
|
||||||
'replace_placeholders' => true,
|
'replace_placeholders' => true,
|
||||||
],
|
],
|
||||||
|
@ -1 +1 @@
|
|||||||
v2.1.1-rel
|
v2.1.2-dev
|
||||||
|
5
public/css/fixes.css
vendored
5
public/css/fixes.css
vendored
@ -252,4 +252,9 @@ select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__
|
|||||||
/* Stop showing a border on our user's drop down menu when open */
|
/* Stop showing a border on our user's drop down menu when open */
|
||||||
.btn-check:checked+.btn, .btn.active, .btn.show, .btn:first-child:active, :not(.btn-check)+.btn:active {
|
.btn-check:checked+.btn, .btn.active, .btn.show, .btn:first-child:active, :not(.btn-check)+.btn:active {
|
||||||
border-color: var(--bs-btn-bg);
|
border-color: var(--bs-btn-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* limit selection to inside the modal */
|
||||||
|
body.modal-open {
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
4
public/js/custom.js
vendored
4
public/js/custom.js
vendored
@ -59,7 +59,7 @@ $(document).ready(function() {
|
|||||||
if (typeof basedn !== 'undefined') {
|
if (typeof basedn !== 'undefined') {
|
||||||
sources = basedn;
|
sources = basedn;
|
||||||
} else {
|
} else {
|
||||||
sources = { url: 'api/bases' };
|
sources = { url: 'ajax/bases' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach the fancytree widget to an existing <div id="tree"> element
|
// Attach the fancytree widget to an existing <div id="tree"> element
|
||||||
@ -95,7 +95,7 @@ $(document).ready(function() {
|
|||||||
source: sources,
|
source: sources,
|
||||||
lazyLoad: function(event,data) {
|
lazyLoad: function(event,data) {
|
||||||
data.result = {
|
data.result = {
|
||||||
url: '/api/children',
|
url: '/ajax/children',
|
||||||
data: {key: data.node.data.item,depth: 1}
|
data: {key: data.node.data.item,depth: 1}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,6 +54,9 @@
|
|||||||
var rendered = false;
|
var rendered = false;
|
||||||
var newadded = [];
|
var newadded = [];
|
||||||
|
|
||||||
|
var oc = $('attribute#objectClass input[type=text]')
|
||||||
|
.map((key,item)=>{return $(item).val()}).toArray();
|
||||||
|
|
||||||
if (newadded.length)
|
if (newadded.length)
|
||||||
process_oc();
|
process_oc();
|
||||||
|
|
||||||
@ -88,7 +91,7 @@
|
|||||||
// Get a list of attributes already on the page, so we dont double up
|
// Get a list of attributes already on the page, so we dont double up
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '{{ url('api/schema/objectclass/attrs') }}/'+item,
|
url: '{{ url('ajax/schema/objectclass/attrs') }}/'+item,
|
||||||
cache: false,
|
cache: false,
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
// Render any must attributes
|
// Render any must attributes
|
||||||
@ -153,7 +156,7 @@
|
|||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '{{ url('api/schema/objectclass/attrs') }}/'+item,
|
url: '{{ url('ajax/schema/objectclass/attrs') }}/'+item,
|
||||||
cache: false,
|
cache: false,
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
var attrs = [];
|
var attrs = [];
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<td>BaseDN(s)</td>
|
<td>BaseDN(s)</td>
|
||||||
<td>
|
<td>
|
||||||
<table class="table table-sm table-borderless">
|
<table class="table table-sm table-borderless">
|
||||||
@foreach($server->baseDNs()->sort(fn($item)=>$item->sort_key) as $item)
|
@foreach($server->baseDNs(TRUE)->sort(fn($item)=>$item->sort_key) as $item)
|
||||||
<tr>
|
<tr>
|
||||||
<td class="ps-0">{{ $item->getDn() }}</td>
|
<td class="ps-0">{{ $item->getDn() }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<td class="p-1 pt-0" rowspan="2">
|
<td class="p-1 pt-0" rowspan="2">
|
||||||
{!! ($x=$o->getObject('jpegphoto')) ? $x->render(FALSE,TRUE) : sprintf('<div class="page-title-icon f32 m-2"><i class="%s"></i></div>',$o->icon() ?? "fas fa-info") !!}
|
{!! ($x=$o->getObject('jpegphoto')) ? $x->render(FALSE,TRUE) : sprintf('<div class="page-title-icon f32 m-2"><i class="%s"></i></div>',$o->icon() ?? "fas fa-info") !!}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end align-bottom pb-0 mb-0 pt-2 pe-3 {{ $x ? 'ps-3' : '' }}"><strong>{{ $o->getDn() }}</strong></td>
|
<td class="text-end align-bottom pb-0 mb-0 pt-2 pe-3 {{ $x ? 'ps-3' : '' }}"><strong class="user-select-all">{{ $o->getDn() }}</strong></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-bottom" style="font-size: 55%" colspan="2">
|
<td class="align-bottom" style="font-size: 55%" colspan="2">
|
||||||
|
@ -103,9 +103,10 @@
|
|||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
@if($step === 2)
|
@if($step === 2)
|
||||||
var oc = {!! $o->getObject('objectclass')->values !!};
|
|
||||||
|
|
||||||
$('#newattr').on('change',function(item) {
|
$('#newattr').on('change',function(item) {
|
||||||
|
var oc = $('attribute#objectClass input[type=text]')
|
||||||
|
.map((key,item)=>{return $(item).val()}).toArray();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '{{ url('entry/attr/add') }}/'+item.target.value,
|
url: '{{ url('entry/attr/add') }}/'+item.target.value,
|
||||||
|
@ -174,7 +174,6 @@
|
|||||||
@section('page-scripts')
|
@section('page-scripts')
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var dn = '{{ $o->getDNSecure() }}';
|
var dn = '{{ $o->getDNSecure() }}';
|
||||||
var oc = {!! $o->getObject('objectclass')->values !!};
|
|
||||||
|
|
||||||
function editmode() {
|
function editmode() {
|
||||||
$('#dn-edit input[name="dn"]').val(dn);
|
$('#dn-edit input[name="dn"]').val(dn);
|
||||||
@ -225,6 +224,9 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('#newattr').on('change',function(item) {
|
$('#newattr').on('change',function(item) {
|
||||||
|
var oc = $('attribute#objectClass input[type=text]')
|
||||||
|
.map((key,item)=>{return $(item).val()}).toArray();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
beforeSend: function() {},
|
beforeSend: function() {},
|
||||||
@ -334,6 +336,11 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#page-modal').on('hide.bs.modal',function() {
|
||||||
|
// Clear any select ranges that occurred while the modal was open
|
||||||
|
document.getSelection().removeAllRanges();
|
||||||
|
});
|
||||||
|
|
||||||
@if(old())
|
@if(old())
|
||||||
editmode();
|
editmode();
|
||||||
@endif
|
@endif
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
@use(App\Classes\LDAP\Server)
|
|
||||||
@extends('layouts.dn')
|
@extends('layouts.dn')
|
||||||
|
|
||||||
@section('page_title')
|
@section('page_title')
|
||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="border-radius: 5px;"><div class="page-title-icon f32"><i class="fas fa-fingerprint"></i></div></td>
|
<td style="border-radius: 5px;"><div class="page-title-icon f32"><i class="fas fa-fingerprint"></i></div></td>
|
||||||
<td class="top text-end align-text-top p-2"><strong>{{ Server::schemaDN() }}</strong></td>
|
<td class="top text-end align-text-top p-2"><strong>{{ $server->schemaDN() }}</strong></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@endsection
|
@endsection
|
||||||
@ -58,7 +57,7 @@
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '{{ url('api/schema/view') }}',
|
url: '{{ url('ajax/schema/view') }}',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { type: type },
|
data: { type: type },
|
||||||
dataType: 'html',
|
dataType: 'html',
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div id="entry_export"></div>
|
<div id="entry_export" style="user-select: text;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
@foreach ($o->getObjects()->filter(fn($item)=>$item->isDirty()) as $key => $oo)
|
@foreach ($o->getObjects()->filter(fn($item)=>$item->isDirty()) as $key => $oo)
|
||||||
<tr>
|
<tr>
|
||||||
<th rowspan="{{ $x=max($oo->values->dot()->keys()->count(),$oo->values_old->dot()->keys()->count())+1}}">
|
<th rowspan="{{ $x=max($oo->values->dot()->keys()->count(),$oo->values_old->dot()->keys()->count())}}">
|
||||||
<abbr title="{{ $oo->description }}">{{ $oo->name }}</abbr>
|
<abbr title="{{ $oo->description }}">{{ $oo->name }}</abbr>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
@ -54,7 +54,7 @@
|
|||||||
<td colspan="2" class="text-center">@lang('Ignoring blank value')</td>
|
<td colspan="2" class="text-center">@lang('Ignoring blank value')</td>
|
||||||
@else
|
@else
|
||||||
<td>{{ (($r=$oo->render_item_old($dotkey)) !== NULL) ? $r : '['.strtoupper(__('New Value')).']' }}</td>
|
<td>{{ (($r=$oo->render_item_old($dotkey)) !== NULL) ? $r : '['.strtoupper(__('New Value')).']' }}</td>
|
||||||
<td>{{ (($r=$oo->render_item_new($dotkey)) !== NULL) ? $r : '['.strtoupper(__('Deleted')).']' }}<input type="hidden" name="{{ $key }}[{{ collect(explode('.',$dotkey))->first() }}][]" value="{{ Arr::get($oo,$dotkey) }}"></td>
|
<td>{{ (($r=$oo->render_item_new($dotkey)) !== NULL) ? $r : '['.strtoupper(__('Deleted')).']' }}<input type="hidden" name="{{ $key }}[{{ $oo->no_attr_tags ? \App\Ldap\Entry::TAG_NOTAG : collect(explode('.',$dotkey))->first() }}][]" value="{{ Arr::get($oo->values->dot(),$dotkey) }}"></td>
|
||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
|
||||||
|
|
||||||
use App\Http\Controllers\APIController;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| API Routes
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Here is where you can register API routes for your application. These
|
|
||||||
| routes are loaded by the RouteServiceProvider within a group which
|
|
||||||
| is assigned the "api" middleware group. Enjoy building your API!
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
Route::controller(APIController::class)->group(function() {
|
|
||||||
Route::get('bases','bases');
|
|
||||||
Route::get('children','children');
|
|
||||||
Route::post('schema/view','schema_view');
|
|
||||||
Route::post('schema/objectclass/attrs/{id}','schema_objectclass_attrs');
|
|
||||||
});
|
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
use App\Http\Controllers\HomeController;
|
use App\Http\Controllers\{AjaxController,HomeController};
|
||||||
use App\Http\Controllers\Auth\LoginController;
|
use App\Http\Controllers\Auth\LoginController;
|
||||||
use App\Http\Middleware\AllowAnonymous;
|
use App\Http\Middleware\AllowAnonymous;
|
||||||
|
|
||||||
@ -57,4 +57,13 @@ Route::controller(HomeController::class)->group(function() {
|
|||||||
Route::view('modal/export/{dn}','modals.entry-export');
|
Route::view('modal/export/{dn}','modals.entry-export');
|
||||||
Route::view('modal/userpassword-check/{dn}','modals.entry-userpassword-check');
|
Route::view('modal/userpassword-check/{dn}','modals.entry-userpassword-check');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::controller(AjaxController::class)
|
||||||
|
->prefix('ajax')
|
||||||
|
->group(function() {
|
||||||
|
Route::get('bases','bases');
|
||||||
|
Route::get('children','children');
|
||||||
|
Route::post('schema/view','schema_view');
|
||||||
|
Route::post('schema/objectclass/attrs/{id}','schema_objectclass_attrs');
|
||||||
|
});
|
@ -12,7 +12,7 @@ class ExampleTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function test_the_application_returns_a_successful_response(): void
|
public function test_the_application_returns_a_successful_response(): void
|
||||||
{
|
{
|
||||||
$response = $this->get('/');
|
$response = $this->get('/login');
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,10 @@ class GetBaseDNTest extends TestCase
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \LdapRecord\Query\ObjectNotFoundException
|
* @throws \LdapRecord\Query\ObjectNotFoundException
|
||||||
* @covers \App\Classes\LDAP\Server::baseDNs()
|
|
||||||
*/
|
*/
|
||||||
public function testBaseDnExists()
|
public function testBaseDnExists()
|
||||||
{
|
{
|
||||||
$o = Server::baseDNs();
|
$o = Server::baseDNs(TRUE);
|
||||||
|
|
||||||
$this->assertIsObject($o);
|
$this->assertIsObject($o);
|
||||||
$this->assertCount(6,$o->toArray());
|
$this->assertCount(6,$o->toArray());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user