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
|
||||
*/
|
||||
class Attribute
|
||||
class Attribute implements \Countable, \ArrayAccess
|
||||
{
|
||||
// Attribute Name
|
||||
protected string $name;
|
||||
@ -41,6 +41,9 @@ class Attribute
|
||||
// RFC3866 Language 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
|
||||
protected $modified = false;
|
||||
@ -98,6 +101,7 @@ class Attribute
|
||||
$this->values = collect($values);
|
||||
$this->lang_tags = collect();
|
||||
$this->required_by = collect();
|
||||
$this->oldValues = collect();
|
||||
|
||||
// No need to load our schema for internal attributes
|
||||
if (! $this->is_internal)
|
||||
@ -151,6 +155,31 @@ class Attribute
|
||||
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
|
||||
*
|
||||
@ -179,6 +208,24 @@ class Attribute
|
||||
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
|
||||
*
|
||||
@ -190,6 +237,7 @@ class Attribute
|
||||
{
|
||||
return view('components.attribute')
|
||||
->with('edit',$edit)
|
||||
->with('new',FALSE)
|
||||
->with('o',$this);
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,15 @@ namespace App\Classes\LDAP\Attribute\Binary;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
use App\Classes\LDAP\Attribute\Binary;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
/**
|
||||
* Represents an JpegPhoto Attribute
|
||||
*/
|
||||
final class JpegPhoto extends Binary
|
||||
{
|
||||
use MD5Updates;
|
||||
|
||||
public function __construct(string $name,array $values)
|
||||
{
|
||||
parent::__construct($name,$values);
|
||||
|
@ -5,12 +5,15 @@ namespace App\Classes\LDAP\Attribute;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
use App\Classes\LDAP\Attribute;
|
||||
use App\Traits\MD5Updates;
|
||||
|
||||
/**
|
||||
* Represents an attribute whose values are passwords
|
||||
*/
|
||||
final class Password extends Attribute
|
||||
{
|
||||
use MD5Updates;
|
||||
|
||||
public function render(bool $edit=FALSE,bool $blank=FALSE): View
|
||||
{
|
||||
return view('components.attribute.password')
|
||||
|
@ -15,6 +15,100 @@ class Entry extends Model
|
||||
/* OVERRIDES */
|
||||
|
||||
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;
|
||||
|
||||
@ -43,6 +137,9 @@ class Entry extends Model
|
||||
// Set required flag
|
||||
$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);
|
||||
}
|
||||
}
|
||||
@ -73,36 +170,12 @@ class Entry extends Model
|
||||
// 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 ($a_key < $b_key) ? -1 : 1;
|
||||
} ])->toArray();
|
||||
} ]);
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
@ -118,6 +191,15 @@ class Entry extends Model
|
||||
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
|
||||
*
|
||||
|
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 -->
|
||||
@if($edit)
|
||||
<div class="input-group has-validation @if($e=$errors->get($o->name_lc))is-invalid @endif">
|
||||
@endif
|
||||
|
||||
<!-- @todo We are not handling redirect backs with updated photos -->
|
||||
<div class="row pt-2">
|
||||
<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">
|
||||
<tr>
|
||||
@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) }}" />
|
||||
<img class="jpegphoto" src="data:{{ $x }};base64, {{ base64_encode($value) }}" @if($e=$errors->get($o->name_lc.'.'.$loop->index))is-invalid @endif />
|
||||
|
||||
@if ($edit)
|
||||
<br><span class="btn btn-sm btn-danger deletable d-none"><i class="fas fa-trash-alt"></i> @lang('Delete')</span>
|
||||
@endif
|
||||
</td>
|
||||
@endswitch
|
||||
@endforeach
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
<!-- @todo TO IMPLEMENT -->
|
||||
<span class="btn btn-sm btn-danger deletable d-none"><i class="fas fa-trash-alt"></i> @lang('Delete')</span>
|
||||
|
||||
@if($edit)
|
||||
<div class="invalid-feedback pb-2">
|
||||
@if($e)
|
||||
{{ join('|',$e) }}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
@endswitch
|
||||
</tr>
|
||||
</div>
|
||||
@endforeach
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- @todo TO IMPLEMENT -->
|
||||
@include('components.attribute.widget.options')
|
||||
</div>
|
||||
</div>
|
@ -1,11 +1,14 @@
|
||||
<!-- $o=Password::class -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<!-- @todo We are not handling redirect backs with updated values -->
|
||||
<div class="row pt-2">
|
||||
<div class="col-1"></div>
|
||||
<div class="col-10 p-2">
|
||||
<div id="{{ $o->name_lc }}">
|
||||
@foreach (old($o->name_lc,$o->values) as $value)
|
||||
@foreach ($o->values as $value)
|
||||
@if ($edit)
|
||||
<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">
|
||||
@if($e)
|
||||
{{ join('|',$e) }}
|
||||
@ -17,9 +20,11 @@
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-lg-4">
|
||||
<span class="btn btn-sm btn-outline-dark mt-3 mb-3"><i class="fas fa-user-check"></i> @lang('Check Password')</span>
|
||||
@include('components.attribute.widget.options')
|
||||
|
||||
<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>
|
@ -1,7 +1,7 @@
|
||||
@if($o->is_rdn)
|
||||
<span class="btn btn-sm btn-outline-focus mt-3"><i class="fas fa-fw fa-exchange"></i> @lang('Rename')</span>
|
||||
@elseif($edit && $o->can_addvalues)
|
||||
<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>
|
||||
@if($new)
|
||||
<script type="text/javascript">
|
||||
@ -15,7 +15,7 @@
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
</div>
|
||||
</span>
|
||||
@endif
|
||||
|
||||
@section('page-scripts')
|
||||
|
@ -3,7 +3,7 @@
|
||||
@section('page_title')
|
||||
<table class="table table-borderless">
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -89,7 +89,7 @@
|
||||
</tr><tr>
|
||||
@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>
|
||||
@endfor
|
||||
</tr>
|
||||
|
Loading…
Reference in New Issue
Block a user