Fix display of password attributes and update processing with jpegphoto and password
This commit is contained in:
parent
c8fffd6d81
commit
c02f390f64
@ -10,7 +10,7 @@ use App\Classes\LDAP\Schema\AttributeType;
|
|||||||
/**
|
/**
|
||||||
* Represents an attribute of an LDAP Object
|
* Represents an attribute of an LDAP Object
|
||||||
*/
|
*/
|
||||||
class Attribute
|
class Attribute implements \Countable, \ArrayAccess
|
||||||
{
|
{
|
||||||
// Attribute Name
|
// Attribute Name
|
||||||
protected string $name;
|
protected string $name;
|
||||||
@ -41,6 +41,9 @@ class Attribute
|
|||||||
// RFC3866 Language Tags
|
// RFC3866 Language Tags
|
||||||
protected Collection $lang_tags;
|
protected Collection $lang_tags;
|
||||||
|
|
||||||
|
// The old values for this attribute - helps with isDirty() to determine if there is an update pending
|
||||||
|
protected Collection $oldValues;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
# Has the attribute been modified
|
# Has the attribute been modified
|
||||||
protected $modified = false;
|
protected $modified = false;
|
||||||
@ -98,6 +101,7 @@ class Attribute
|
|||||||
$this->values = collect($values);
|
$this->values = collect($values);
|
||||||
$this->lang_tags = collect();
|
$this->lang_tags = collect();
|
||||||
$this->required_by = collect();
|
$this->required_by = collect();
|
||||||
|
$this->oldValues = collect();
|
||||||
|
|
||||||
// No need to load our schema for internal attributes
|
// No need to load our schema for internal attributes
|
||||||
if (! $this->is_internal)
|
if (! $this->is_internal)
|
||||||
@ -151,6 +155,31 @@ class Attribute
|
|||||||
return $this->__get('name');
|
return $this->__get('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return $this->values->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetExists(mixed $offset): bool
|
||||||
|
{
|
||||||
|
return ! is_null($this->values->get($offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetGet(mixed $offset): mixed
|
||||||
|
{
|
||||||
|
return $this->values->get($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetSet(mixed $offset, mixed $value): void
|
||||||
|
{
|
||||||
|
// We cannot set new values using array syntax
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetUnset(mixed $offset): void
|
||||||
|
{
|
||||||
|
// We cannot clear values using array syntax
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the hints about this attribute, ie: RDN, Required, etc
|
* Return the hints about this attribute, ie: RDN, Required, etc
|
||||||
*
|
*
|
||||||
@ -179,6 +208,24 @@ class Attribute
|
|||||||
return $result->toArray();
|
return $result->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this attribute has changes
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isDirty(): bool
|
||||||
|
{
|
||||||
|
if ($this->oldValues->count() !== $this->values->count())
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return $this->values->diff($this->oldValues)->count() !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function oldValues(array $array): void
|
||||||
|
{
|
||||||
|
$this->oldValues = collect($array);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the attribute value
|
* Display the attribute value
|
||||||
*
|
*
|
||||||
@ -190,6 +237,7 @@ class Attribute
|
|||||||
{
|
{
|
||||||
return view('components.attribute')
|
return view('components.attribute')
|
||||||
->with('edit',$edit)
|
->with('edit',$edit)
|
||||||
|
->with('new',FALSE)
|
||||||
->with('o',$this);
|
->with('o',$this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,12 +5,15 @@ namespace App\Classes\LDAP\Attribute\Binary;
|
|||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
use App\Classes\LDAP\Attribute\Binary;
|
use App\Classes\LDAP\Attribute\Binary;
|
||||||
|
use App\Traits\MD5Updates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an JpegPhoto Attribute
|
* Represents an JpegPhoto Attribute
|
||||||
*/
|
*/
|
||||||
final class JpegPhoto extends Binary
|
final class JpegPhoto extends Binary
|
||||||
{
|
{
|
||||||
|
use MD5Updates;
|
||||||
|
|
||||||
public function __construct(string $name,array $values)
|
public function __construct(string $name,array $values)
|
||||||
{
|
{
|
||||||
parent::__construct($name,$values);
|
parent::__construct($name,$values);
|
||||||
|
@ -5,12 +5,15 @@ namespace App\Classes\LDAP\Attribute;
|
|||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
use App\Classes\LDAP\Attribute;
|
use App\Classes\LDAP\Attribute;
|
||||||
|
use App\Traits\MD5Updates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an attribute whose values are passwords
|
* Represents an attribute whose values are passwords
|
||||||
*/
|
*/
|
||||||
final class Password extends Attribute
|
final class Password extends Attribute
|
||||||
{
|
{
|
||||||
|
use MD5Updates;
|
||||||
|
|
||||||
public function render(bool $edit=FALSE,bool $blank=FALSE): View
|
public function render(bool $edit=FALSE,bool $blank=FALSE): View
|
||||||
{
|
{
|
||||||
return view('components.attribute.password')
|
return view('components.attribute.password')
|
||||||
|
@ -15,6 +15,100 @@ class Entry extends Model
|
|||||||
/* OVERRIDES */
|
/* OVERRIDES */
|
||||||
|
|
||||||
public function getAttributes(): array
|
public function getAttributes(): array
|
||||||
|
{
|
||||||
|
return $this->getAttributesAsObjects()->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the new and old values for a given key are equivalent.
|
||||||
|
*/
|
||||||
|
protected function originalIsEquivalent(string $key): bool
|
||||||
|
{
|
||||||
|
if (! array_key_exists($key, $this->original)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = $this->attributes[$key];
|
||||||
|
$original = $this->original[$key];
|
||||||
|
|
||||||
|
if ($current === $original) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//dump(['key'=>$key,'current'=>$current,'original'=>$this->original[$key],'objectvalue'=>$this->getAttributeAsObject($key)->isDirty()]);
|
||||||
|
return ! $this->getAttributeAsObject($key)->isDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOriginal(): array
|
||||||
|
{
|
||||||
|
static $result = NULL;
|
||||||
|
|
||||||
|
if (is_null($result)) {
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
// @todo Optimise this foreach with getAttributes()
|
||||||
|
foreach (parent::getOriginal() as $attribute => $value) {
|
||||||
|
// If the attribute name has language tags
|
||||||
|
$matches = [];
|
||||||
|
if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
|
||||||
|
$attribute = $matches[1];
|
||||||
|
|
||||||
|
// If the attribute doesnt exist we'll create it
|
||||||
|
$o = Arr::get($result,$attribute,Factory::create($attribute,[]));
|
||||||
|
$o->setLangTag($matches[3],$value);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$o = Factory::create($attribute,$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $result->has($attribute)) {
|
||||||
|
// Set the rdn flag
|
||||||
|
if (preg_match('/^'.$attribute.'=/i',$this->dn))
|
||||||
|
$o->setRDN();
|
||||||
|
|
||||||
|
// Set required flag
|
||||||
|
$o->required_by(collect($this->getAttribute('objectclass')));
|
||||||
|
|
||||||
|
$result->put($attribute,$o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ATTRIBUTES */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a key to use for sorting
|
||||||
|
*
|
||||||
|
* @todo This should be the DN in reverse order
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSortKeyAttribute(): string
|
||||||
|
{
|
||||||
|
return $this->getDn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* METHODS */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an attribute as an object
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return Attribute|null
|
||||||
|
*/
|
||||||
|
public function getAttributeAsObject(string $key): Attribute|null
|
||||||
|
{
|
||||||
|
return Arr::get($this->getAttributesAsObjects(),$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert all our attribute values into an array of Objects
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
protected function getAttributesAsObjects(): Collection
|
||||||
{
|
{
|
||||||
static $result = NULL;
|
static $result = NULL;
|
||||||
|
|
||||||
@ -43,6 +137,9 @@ class Entry extends Model
|
|||||||
// Set required flag
|
// Set required flag
|
||||||
$o->required_by(collect($this->getAttribute('objectclass')));
|
$o->required_by(collect($this->getAttribute('objectclass')));
|
||||||
|
|
||||||
|
// Store our original value to know if this attribute has changed
|
||||||
|
$o->oldValues(Arr::get($this->original,$attribute));
|
||||||
|
|
||||||
$result->put($attribute,$o);
|
$result->put($attribute,$o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,36 +170,12 @@ class Entry extends Model
|
|||||||
// Case where at least one attribute or its friendly name is in $attrs_display_order
|
// Case where at least one attribute or its friendly name is in $attrs_display_order
|
||||||
// return -1 if $a before $b in $attrs_display_order
|
// return -1 if $a before $b in $attrs_display_order
|
||||||
return ($a_key < $b_key) ? -1 : 1;
|
return ($a_key < $b_key) ? -1 : 1;
|
||||||
} ])->toArray();
|
} ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ATTRIBUTES */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a key to use for sorting
|
|
||||||
*
|
|
||||||
* @todo This should be the DN in reverse order
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getSortKeyAttribute(): string
|
|
||||||
{
|
|
||||||
return $this->getDn();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* METHODS */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a secure version of the DN
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDNSecure(): string
|
|
||||||
{
|
|
||||||
return Crypt::encryptString($this->getDn());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of available attributes - as per the objectClass entry of the record
|
* Return a list of available attributes - as per the objectClass entry of the record
|
||||||
*
|
*
|
||||||
@ -118,6 +191,15 @@ class Entry extends Model
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a secure version of the DN
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDNSecure(): string
|
||||||
|
{
|
||||||
|
return Crypt::encryptString($this->getDn());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of LDAP internal attributes
|
* Return a list of LDAP internal attributes
|
||||||
*
|
*
|
||||||
|
23
app/Traits/MD5Updates.php
Normal file
23
app/Traits/MD5Updates.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a value has changed by comparing its MD5 value
|
||||||
|
*/
|
||||||
|
namespace App\Traits;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
trait MD5Updates
|
||||||
|
{
|
||||||
|
public function isDirty(): bool
|
||||||
|
{
|
||||||
|
if (! parent::isDirty())
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
foreach ($this->values->diff($this->oldValues) as $key => $value)
|
||||||
|
if (md5(Arr::get($this->oldValues,$key)) !== $value)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,40 @@
|
|||||||
<!-- $o=Binary\JpegPhoto::class -->
|
<!-- $o=Binary\JpegPhoto::class -->
|
||||||
@if($edit)
|
<!-- @todo We are not handling redirect backs with updated photos -->
|
||||||
<div class="input-group has-validation @if($e=$errors->get($o->name_lc))is-invalid @endif">
|
<div class="row pt-2">
|
||||||
@endif
|
<div class="col-1"></div>
|
||||||
|
<div class="col-10 p-2">
|
||||||
|
<div id="{{ $o->name_lc }}">
|
||||||
|
<table class="table table-borderless p-0 m-0">
|
||||||
|
@foreach ($o->values as $value)
|
||||||
|
<div class="input-group has-validation">
|
||||||
|
<tr>
|
||||||
|
@switch ($x=$f->buffer($value,FILEINFO_MIME_TYPE))
|
||||||
|
@case('image/jpeg')
|
||||||
|
@default
|
||||||
|
<td>
|
||||||
|
<input type="hidden" name="{{ $o->name_lc }}[]" value="{{ md5($value) }}">
|
||||||
|
<img class="jpegphoto" src="data:{{ $x }};base64, {{ base64_encode($value) }}" @if($e=$errors->get($o->name_lc.'.'.$loop->index))is-invalid @endif />
|
||||||
|
|
||||||
<table class="table table-borderless p-0 m-0">
|
@if ($edit)
|
||||||
<tr>
|
<br>
|
||||||
@foreach ($o->values as $value)
|
<!-- @todo TO IMPLEMENT -->
|
||||||
@switch ($x=$f->buffer($value,FILEINFO_MIME_TYPE))
|
<span class="btn btn-sm btn-danger deletable d-none"><i class="fas fa-trash-alt"></i> @lang('Delete')</span>
|
||||||
@case('image/jpeg')
|
|
||||||
@default
|
|
||||||
<td>
|
|
||||||
<input type="hidden" name="{{ $o->name_lc }}[]" value="{{ md5($value) }}">
|
|
||||||
<img class="jpegphoto" src="data:{{ $x }};base64, {{ base64_encode($value) }}" />
|
|
||||||
|
|
||||||
@if($edit)
|
<div class="invalid-feedback pb-2">
|
||||||
<br><span class="btn btn-sm btn-danger deletable d-none"><i class="fas fa-trash-alt"></i> @lang('Delete')</span>
|
@if($e)
|
||||||
@endif
|
{{ join('|',$e) }}
|
||||||
</td>
|
@endif
|
||||||
@endswitch
|
</div>
|
||||||
@endforeach
|
@endif
|
||||||
</tr>
|
</td>
|
||||||
</table>
|
@endswitch
|
||||||
|
</tr>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if($edit)
|
<!-- @todo TO IMPLEMENT -->
|
||||||
<div class="invalid-feedback pb-2">
|
@include('components.attribute.widget.options')
|
||||||
@if($e)
|
|
||||||
{{ join('|',$e) }}
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@endif
|
|
@ -1,11 +1,14 @@
|
|||||||
<!-- $o=Password::class -->
|
<!-- $o=Password::class -->
|
||||||
<div class="row">
|
<!-- @todo We are not handling redirect backs with updated values -->
|
||||||
<div class="col-12">
|
<div class="row pt-2">
|
||||||
|
<div class="col-1"></div>
|
||||||
|
<div class="col-10 p-2">
|
||||||
<div id="{{ $o->name_lc }}">
|
<div id="{{ $o->name_lc }}">
|
||||||
@foreach (old($o->name_lc,$o->values) as $value)
|
@foreach ($o->values as $value)
|
||||||
@if ($edit)
|
@if ($edit)
|
||||||
<div class="input-group has-validation">
|
<div class="input-group has-validation">
|
||||||
<input type="password" class="form-control @if($e=$errors->get($o->name_lc.'.'.$loop->index))is-invalid @endif mb-1 @if($o->values->search($value) === FALSE) border-focus @endif" name="{{ $o->name_lc }}[]" value="{{ str_repeat('*',10) }}" readonly="true">
|
<input type="password" class="form-control @if($e=$errors->get($o->name_lc.'.'.$loop->index))is-invalid @endif mb-1 @if($o->values->search($value) === FALSE) border-focus @endif" name="{{ $o->name_lc }}[]" value="{{ md5($value) }}" readonly="true">
|
||||||
|
|
||||||
<div class="invalid-feedback pb-2">
|
<div class="invalid-feedback pb-2">
|
||||||
@if($e)
|
@if($e)
|
||||||
{{ join('|',$e) }}
|
{{ join('|',$e) }}
|
||||||
@ -17,9 +20,11 @@
|
|||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12 col-sm-6 col-lg-4">
|
@include('components.attribute.widget.options')
|
||||||
<span class="btn btn-sm btn-outline-dark mt-3 mb-3"><i class="fas fa-user-check"></i> @lang('Check Password')</span>
|
|
||||||
|
<span class="p-0 m-0">
|
||||||
|
<span class="btn btn-sm btn-outline-dark mt-3"><i class="fas fa-user-check"></i> @lang('Check Password')</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -1,7 +1,7 @@
|
|||||||
@if($o->is_rdn)
|
@if($o->is_rdn)
|
||||||
<span class="btn btn-sm btn-outline-focus mt-3"><i class="fas fa-fw fa-exchange"></i> @lang('Rename')</span>
|
<span class="btn btn-sm btn-outline-focus mt-3"><i class="fas fa-fw fa-exchange"></i> @lang('Rename')</span>
|
||||||
@elseif($edit && $o->can_addvalues)
|
@elseif($edit && $o->can_addvalues)
|
||||||
<div class="p-0 m-0">
|
<span class="p-0 m-0">
|
||||||
<span class="btn btn-sm btn-outline-primary mt-3 addable @if(! $new)d-none @endif" id="{{ $o->name_lc }}"><i class="fas fa-fw fa-plus"></i> @lang('Add Value')</span>
|
<span class="btn btn-sm btn-outline-primary mt-3 addable @if(! $new)d-none @endif" id="{{ $o->name_lc }}"><i class="fas fa-fw fa-plus"></i> @lang('Add Value')</span>
|
||||||
@if($new)
|
@if($new)
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
@ -15,7 +15,7 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</span>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@section('page-scripts')
|
@section('page-scripts')
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
@section('page_title')
|
@section('page_title')
|
||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="{{ ($x=Arr::get($o->getAttributes(),'jpegphoto')) ? 'border' : '' }}" rowspan="2">{!! $x ? $x->render() : sprintf('<div class="page-title-icon f32"><i class="%s"></i></div>',$o->icon() ?? "fas fa-info") !!}</td>
|
<td class="{{ ($x=Arr::get($o->getOriginal(),'jpegphoto')) ? 'border' : '' }}" rowspan="2">{!! $x ? $x->render() : sprintf('<div class="page-title-icon f32"><i class="%s"></i></div>',$o->icon() ?? "fas fa-info") !!}</td>
|
||||||
<td class="text-end align-text-top p-0 {{ $x ? 'ps-5' : 'pt-2' }}"><strong>{{ $dn }}</strong></td>
|
<td class="text-end align-text-top p-0 {{ $x ? 'ps-5' : 'pt-2' }}"><strong>{{ $dn }}</strong></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
</tr><tr>
|
</tr><tr>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<td>{{ Arr::get(Arr::get($o->getOriginal(),$key,['['.strtoupper(__('New Value')).']']),$xx) }}</td>
|
<td>{{ Arr::get(Arr::get($o->getOriginal(),$key),$xx,'['.strtoupper(__('New Value')).']') }}</td>
|
||||||
<td>{{ $y=Arr::get($value,$xx) }}<input type="hidden" name="{{ $key }}[]" value="{{ $y }}"></td>
|
<td>{{ $y=Arr::get($value,$xx) }}<input type="hidden" name="{{ $key }}[]" value="{{ $y }}"></td>
|
||||||
@endfor
|
@endfor
|
||||||
</tr>
|
</tr>
|
||||||
|
Loading…
Reference in New Issue
Block a user