Improved handling for Kerberous attributes - closes #154

This commit is contained in:
Deon George 2025-03-12 21:07:16 +11:00
parent 853bd92340
commit add3f85812
17 changed files with 280 additions and 5 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,106 @@
<!-- $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);
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')