Compare commits

...

6 Commits

Author SHA1 Message Date
3cc577136c Some jquery selector changes, change some button spans to buttons, set readonly on the form for attribute javascript, fix krbTicketFlags to only be changed when in edit mode
All checks were successful
Create Docker Image / Test Application (x86_64) (push) Successful in 29s
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 1m24s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 4m33s
Create Docker Image / Final Docker Image Manifest (push) Successful in 11s
2025-03-12 22:52:14 +11:00
b3307835f4 Fix password hash select list, was not being editable when choosing edit mode 2025-03-12 22:52:14 +11:00
babe1f6fc5 Improved handling for Kerberous attributes - closes #154 2025-03-12 22:52:14 +11:00
e163f4ca5e Fix detection of zero values when rendering update NEW/DELETED tags 2025-03-12 18:01:00 +11:00
d1fb2359cf Add some opendj internal attributes. Remove some unused variables in APIController 2025-03-11 21:02:11 +11:00
f3a03ac858 Revert version to 2.0.3-dev
All checks were successful
Create Docker Image / Test Application (x86_64) (push) Successful in 29s
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 1m27s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 4m38s
Create Docker Image / Final Docker Image Manifest (push) Successful in 9s
2025-03-11 20:14:13 +11:00
26 changed files with 337 additions and 18 deletions

View File

@ -3,7 +3,7 @@ run-name: ${{ gitea.actor }} Building Docker Image 🐳
on: [push]
env:
DOCKER_HOST: tcp://127.0.0.1:2375
ASSETS: 509b1a1
ASSETS: c2780a3
jobs:
test:

View File

@ -279,7 +279,11 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
*/
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
{
return view('components.attribute')
$view = view()->exists($x='components.attribute.'.$this->name_lc)
? view($x)
: view('components.attribute');
return $view
->with('o',$this)
->with('edit',$edit)
->with('old',$old)

View File

@ -26,12 +26,17 @@ class Factory
'entrycsn' => Internal\CSN::class,
'entrydn' => Internal\DN::class,
'entryuuid' => Internal\UUID::class,
'etag' => Internal\Etag::class,
'krbprincipalkey' => KrbPrincipalKey::class,
'krbticketflags' => KrbTicketFlags::class,
'gidnumber' => GidNumber::class,
'hassubordinates' => Internal\HasSubordinates::class,
'jpegphoto' => Binary\JpegPhoto::class,
'modifytimestamp' => Internal\Timestamp::class,
'modifiersname' => Internal\DN::class,
'numsubordinates' => Internal\NumSubordinates::class,
'objectclass' => ObjectClass::class,
'pwdpolicysubentry' => Internal\PwdPolicySubentry::class,
'structuralobjectclass' => Internal\StructuralObjectClass::class,
'subschemasubentry' => Internal\SubschemaSubentry::class,
'supportedcontrol' => Schema\OID::class,

View File

@ -0,0 +1,12 @@
<?php
namespace App\Classes\LDAP\Attribute\Internal;
use App\Classes\LDAP\Attribute\Internal;
/**
* Represents an Etag Attribute
*/
final class Etag extends Internal
{
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Classes\LDAP\Attribute\Internal;
use App\Classes\LDAP\Attribute\Internal;
/**
* Represents an NumSubordinates Attribute
*/
final class NumSubordinates extends Internal
{
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Classes\LDAP\Attribute\Internal;
use App\Classes\LDAP\Attribute\Internal;
/**
* Represents an PwdPolicySubentry Attribute
*/
final class PwdPolicySubentry extends Internal
{
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Classes\LDAP\Attribute;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Arr;
use App\Classes\LDAP\Attribute;
use App\Traits\MD5Updates;
/**
* Represents an attribute whose values are passwords
*/
final class KrbPrincipalKey extends Attribute
{
use MD5Updates;
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
{
return view('components.attribute.krbprincipalkey')
->with('o',$this)
->with('edit',$edit)
->with('old',$old)
->with('new',$new);
}
public function render_item_old(int $key): ?string
{
$pw = Arr::get($this->oldValues,$key);
return $pw
? str_repeat('*',16)
: NULL;
}
public function render_item_new(int $key): ?string
{
$pw = Arr::get($this->values,$key);
return $pw
? str_repeat('*',16)
: NULL;
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace App\Classes\LDAP\Attribute;
use Illuminate\Contracts\View\View;
use App\Classes\LDAP\Attribute;
use Illuminate\Support\Collection;
/**
* Represents an attribute whose value is a Kerberos Ticket Flag
* See RFC4120
*/
class KrbTicketFlags extends Attribute
{
private const DISALLOW_POSTDATED = 0x00000001;
private const DISALLOW_FORWARDABLE = 0x00000002;
private const DISALLOW_TGT_BASED = 0x00000004;
private const DISALLOW_RENEWABLE = 0x00000008;
private const DISALLOW_PROXIABLE = 0x00000010;
private const DISALLOW_DUP_SKEY = 0x00000020;
private const DISALLOW_ALL_TIX = 0x00000040;
private const REQUIRES_PRE_AUTH = 0x00000080;
private const REQUIRES_HW_AUTH = 0x00000100;
private const REQUIRES_PWCHANGE = 0x00000200;
private const DISALLOW_SVR = 0x00001000;
private const PWCHANGE_SERVICE = 0x00002000;
private static function helpers(): Collection
{
$helpers = collect([
log(self::DISALLOW_POSTDATED,2) => __('KRB_DISALLOW_POSTDATED'),
log(self::DISALLOW_FORWARDABLE,2) => __('KRB_DISALLOW_FORWARDABLE'),
log(self::DISALLOW_TGT_BASED,2) => __('KRB_DISALLOW_TGT_BASED'),
log(self::DISALLOW_RENEWABLE,2) => __('KRB_DISALLOW_RENEWABLE'),
log(self::DISALLOW_PROXIABLE,2) => __('KRB_DISALLOW_PROXIABLE'),
log(self::DISALLOW_DUP_SKEY,2) => __('KRB_DISALLOW_DUP_SKEY'),
log(self::DISALLOW_ALL_TIX,2) => __('KRB_DISALLOW_ALL_TIX'),
log(self::REQUIRES_PRE_AUTH,2) => __('KRB_REQUIRES_PRE_AUTH'),
log(self::REQUIRES_HW_AUTH,2) => __('KRB_REQUIRES_HW_AUTH'),
log(self::REQUIRES_PWCHANGE,2) => __('KRB_REQUIRES_PWCHANGE'),
log(self::DISALLOW_SVR,2) => __('KRB_DISALLOW_SVR'),
log(self::PWCHANGE_SERVICE,2) => __('KRB_PWCHANGE_SERVICE'),
])
->replace(config('pla.krb.bits',[]));
return $helpers;
}
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
{
return view('components.attribute.krbticketflags')
->with('o',$this)
->with('edit',$edit)
->with('old',$old)
->with('new',$new)
->with('helper',static::helpers());
}
}

View File

@ -39,14 +39,13 @@ class APIController extends Controller
*/
public function children(Request $request): Collection
{
$levels = $request->query('depth',1);
$dn = Crypt::decryptString($request->query('key'));
// Sometimes our key has a command, so we'll ignore it
if (str_starts_with($dn,'*') && ($x=strpos($dn,'|')))
$dn = substr($dn,$x+1);
Log::debug(sprintf('%s: Query [%s] - Levels [%d]',__METHOD__,$dn,$levels));
Log::debug(sprintf('%s: Query [%s]',__METHOD__,$dn));
return (config('server'))
->children($dn)
@ -102,7 +101,7 @@ class APIController extends Controller
* @param string $objectclass
* @return array
*/
public function schema_objectclass_attrs(Request $request,string $objectclass): array
public function schema_objectclass_attrs(string $objectclass): array
{
$oc = config('server')->schema('objectclasses',$objectclass);

17
package-lock.json generated
View File

@ -10,6 +10,7 @@
"animate-sass": "^0.8.2",
"axios": "^1.3.4",
"bootstrap": "^5.2.3",
"bootstrap-icons": "^1.11.3",
"jquery": "^3.6.3",
"jquery-ui": "^1.13.2",
"jquery.fancytree": "^2.38.3",
@ -3053,6 +3054,22 @@
"@popperjs/core": "^2.11.8"
}
},
"node_modules/bootstrap-icons": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
"integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/twbs"
},
{
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
}
],
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",

View File

@ -15,6 +15,7 @@
"animate-sass": "^0.8.2",
"axios": "^1.3.4",
"bootstrap": "^5.2.3",
"bootstrap-icons": "^1.11.3",
"jquery": "^3.6.3",
"jquery-ui": "^1.13.2",
"jquery.fancytree": "^2.38.3",

View File

@ -1 +1 @@
v2.0.2-rel
v2.0.3-dev

View File

@ -7,3 +7,6 @@
// Select2
@import "select2/dist/css/select2";
@import "select2-bootstrap-5-theme/dist/select2-bootstrap-5-theme";
// Bootstrap icons
@import "bootstrap-icons"

View File

@ -17,4 +17,6 @@
<x-attribute :o="$o" :edit="true" :new="$new ?? FALSE"/>
</div>
</div>
</div>
@yield($o->name_lc.'-scripts')

View File

@ -0,0 +1,8 @@
<!-- $o=Attribute::class -->
<x-attribute.layout :edit="false" :new="false" :detail="true" :o="$o">
@foreach(old($o->name_lc,($new ?? FALSE) ? [NULL] : $o->values) as $value)
<div class="input-group">
<input type="text" @class(['form-control','mb-1']) name="{{ $o->name_lc }}[]" value="{{ \Carbon\Carbon::createFromTimestamp(strtotime($value))->format(config('pla.datetime_format','Y-m-d H:i:s')) }}" @disabled(true)>
</div>
@endforeach
</x-attribute.layout>

View File

@ -0,0 +1 @@
@include('components.attribute.krblastfailedauth')

View File

@ -0,0 +1 @@
@include('components.attribute.krblastfailedauth')

View File

@ -0,0 +1,8 @@
<!-- $o=Attribute::class -->
<x-attribute.layout :edit="false" :new="false" :detail="true" :o="$o">
@foreach(old($o->name_lc,($new ?? FALSE) ? [NULL] : $o->values) as $value)
<div class="input-group">
<input type="text" @class(['form-control','mb-1']) name="{{ $o->name_lc }}[]" value="{{ $value }}" @disabled(true)>
</div>
@endforeach
</x-attribute.layout>

View File

@ -0,0 +1 @@
@include('components.attribute.krblastfailedauth')

View File

@ -0,0 +1,18 @@
<!-- $o=Password::class -->
<x-attribute.layout :edit="$edit ?? FALSE" :new="$new ?? FALSE" :o="$o">
@foreach($o->values as $value)
@if($edit)
<div class="input-group has-validation mb-3">
<input type="password" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$loop->index)),'mb-1','border-focus'=>$o->values->contains($value)]) name="{{ $o->name_lc }}[]" value="{{ md5($value) }}" @readonly(true)>
<div class="invalid-feedback pb-2">
@if($e)
{{ join('|',$e) }}
@endif
</div>
</div>
@else
{{ str_repeat('*',16) }}
@endif
@endforeach
</x-attribute.layout>

View File

@ -0,0 +1,109 @@
<!-- $o=KrbTicketFlags::class -->
<x-attribute.layout :edit="$edit ?? FALSE" :new="$new ?? FALSE" :o="$o">
@foreach(($o->values->count() ? $o->values : ($new ? [0] : NULL)) as $value)
@if($edit)
<div id="32"></div>
<div id="16"></div>
<div class="input-group has-validation mb-3">
<input type="hidden" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$loop->index)),'mb-1','border-focus'=>$o->values->contains($value)]) name="{{ $o->name_lc }}[]" value="{{ $value }}" @readonly(true)>
<div class="invalid-feedback pb-2">
@if($e)
{{ join('|',$e) }}
@endif
</div>
</div>
@else
{{ $value }}
@endif
@endforeach
</x-attribute.layout>
@section($o->name_lc.'-scripts')
<script type="text/javascript">
var value = {{ $value ?? 0 }};
var label = {!! $helper !!};
function tooltip(bit) {
if (bit === undefined)
return;
return label[bit] ? label[bit] : 'Bit '+bit;
}
function binary(s=31,e=0) {
var result = '';
for (let x=s;x>=e;x--) {
var bit = (value&Math.pow(2,x));
result += '<i id="b'+x+'" style="margin-left:-1px;" class="fs-4 bi bi-'+(bit ? '1' : '0')+'-square'+(bit ? '-fill' : '')+'" data-bs-toggle="tooltip" data-bs-placement="bottom" title="'+tooltip(x)+'"></i>';
}
return result;
}
function krbticketflags() {
$('div#32').append(binary(31,16));
$('div#16').append(binary(15,0));
$('attribute#krbTicketFlags').find('i')
.on('click',function() {
var item = $(this);
if ($('form#dn-edit').attr('readonly'))
return;
var key = Number(item.attr('id').substring(1));
if (item.data('old') === undefined)
item.data('old',null);
item.toggleClass('text-success');
// has the item changed?
if (item.data('old') === null) {
// It was set to 1
if (item.hasClass('bi-1-square-fill')) {
item.data('old',1);
item.removeClass('bi-1-square-fill').addClass('bi-0-square-fill');
value -= Math.pow(2,key);
// It was set to 0
} else if (item.hasClass('bi-0-square')) {
item.data('old',0);
item.removeClass('bi-0-square').addClass('bi-1-square-fill');
value += Math.pow(2,key);
}
} else {
if (item.data('old') === 0) {
item.removeClass('bi-1-square-fill').addClass('bi-0-square');
value -= Math.pow(2,key);
} else {
item.removeClass('bi-0-square-fill').addClass('bi-1-square-fill');
value += Math.pow(2,key);
}
item.data('old',null);
}
$('attribute#krbTicketFlags').find('input').val(value);
});
}
// When returning to a Entry after an update, jquery hasnt loaded yet, so make sure we defer this to after the page has run
if (window.$ === undefined) {
document.addEventListener('DOMContentLoaded',() => krbticketflags());
} else {
krbticketflags();
$('attribute#krbTicketFlags').find('i')
.tooltip();
}
</script>
@endsection

View File

@ -1,5 +1,5 @@
<div class="row pt-2">
<div @class(['col-1','d-none'=>(! $edit)])></div>
<div @class(['col-1','d-none'=>(! $edit) && (! ($detail ?? true))])></div>
<div class="col-10 p-2">
<attribute id="{{ $o->name }}">
{{ $slot }}
@ -7,4 +7,6 @@
<x-attribute.widget.options :o="$o" :edit="$edit" :new="$new"/>
</div>
</div>
</div>
@yield($o->name_lc.'-scripts')

View File

@ -220,7 +220,7 @@
<!-- All other attributes -->
@default
@php($clone=TRUE)
<span @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name }}-addnew"><i class="fas fa-fw fa-plus"></i> @lang('Add Value')</span>
<button @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name }}-addnew"><i class="fas fa-fw fa-plus"></i> @lang('Add Value')</button>
@section('page-scripts')
@if($clone && $edit && $o->can_addvalues)

View File

@ -85,7 +85,7 @@
});
// Our password type
$('div#userPassword .form-select').each(function() {
$('attribute#userPassword .form-select').each(function() {
$(this).prop('disabled',false);
})

View File

@ -23,7 +23,7 @@
<div class="tab-content">
<!-- All Attributes -->
<div class="tab-pane active" id="attributes" role="tabpanel">
<form id="dn-edit" method="POST" class="needs-validation" action="{{ url('entry/update/pending') }}" novalidate>
<form id="dn-edit" method="POST" class="needs-validation" action="{{ url('entry/update/pending') }}" novalidate readonly>
@csrf
<input type="hidden" name="dn" value="">
@ -156,9 +156,12 @@
function editmode() {
$('#dn-edit input[name="dn"]').val(dn);
$('form#dn-edit').attr('readonly',false);
$('button[id=entry-edit]')
.removeClass('btn-outline-dark')
.addClass('btn-dark');
.addClass('btn-dark')
.addClass('opacity-100')
.attr('disabled',true);
// Find all input items and turn off readonly
$('input.form-control').each(function() {
@ -170,13 +173,13 @@
});
// Our password type
$('div#userPassword .form-select').each(function() {
$('attribute#userPassword .form-select').each(function() {
$(this).prop('disabled',false);
})
$('.row.d-none').removeClass('d-none');
$('.addable.d-none').removeClass('d-none');
$('.deletable.d-none').removeClass('d-none');
$('button.addable.d-none').removeClass('d-none');
$('button.deletable.d-none').removeClass('d-none');
@if($o->getMissingAttributes()->count())
$('#newattr-select.d-none').removeClass('d-none');

View File

@ -39,8 +39,8 @@
</tr><tr>
@endif
<td>{{ $oo->render_item_old($xx) ?: '['.strtoupper(__('New Value')).']' }}</td>
<td>{{ $oo->render_item_new($xx) ?: '['.strtoupper(__('Deleted')).']' }}<input type="hidden" name="{{ $key }}[]" value="{{ Arr::get($oo->values,$xx) }}"></td>
<td>{{ (($r=$oo->render_item_old($xx)) !== NULL) ? $r : '['.strtoupper(__('New Value')).']' }}</td>
<td>{{ (($r=$oo->render_item_new($xx)) !== NULL) ? $r : '['.strtoupper(__('Deleted')).']' }}<input type="hidden" name="{{ $key }}[]" value="{{ Arr::get($oo->values,$xx) }}"></td>
@endfor
</tr>
@endforeach