Compare commits

...

3 Commits

Author SHA1 Message Date
240b46aa9e 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 30s
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 1m28s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 4m33s
Create Docker Image / Final Docker Image Manifest (push) Successful in 10s
2025-03-12 22:28:23 +11:00
0dd74146cb Fix password hash select list, was not being editable when choosing edit mode 2025-03-12 22:00:18 +11:00
55f08bd959 Improved handling for Kerberous attributes - closes #154
All checks were successful
Create Docker Image / Test Application (x86_64) (push) Successful in 3m36s
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 1m38s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 4m35s
Create Docker Image / Final Docker Image Manifest (push) Successful in 9s
2025-03-12 21:29:15 +11:00
20 changed files with 293 additions and 12 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

@ -27,6 +27,8 @@ class Factory
'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,

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());
}
}

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

@ -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 ($ === 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');