Skip to content

Commit c4d28c8

Browse files
committed
Add support for displaying user certificates, that are recorded in the directory with a ;binary tag. Closes #75
1 parent 29c460f commit c4d28c8

File tree

8 files changed

+122
-11
lines changed

8 files changed

+122
-11
lines changed

app/Classes/LDAP/Attribute.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ class Attribute implements \Countable, \ArrayAccess
1616
{
1717
// Attribute Name
1818
protected string $name;
19-
private int $counter = 0;
2019

2120
// Is this attribute an internal attribute
2221
protected(set) bool $is_internal = FALSE;

app/Classes/LDAP/Attribute/Factory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class Factory
5252
'supportedfeatures' => Schema\OID::class,
5353
'supportedldapversion' => Schema\Generic::class,
5454
'supportedsaslmechanisms' => Schema\Mechanisms::class,
55+
'usercertificate' => UserCertificate::class,
5556
'userpassword' => Password::class,
5657
];
5758

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace App\Classes\LDAP\Attribute;
4+
5+
use Carbon\Carbon;
6+
use Illuminate\Support\Arr;
7+
8+
use App\Classes\LDAP\Attribute;
9+
use App\Traits\MD5Updates;
10+
11+
/**
12+
* Represents an attribute whose values is a binary user certificate
13+
*/
14+
final class UserCertificate extends Attribute
15+
{
16+
use MD5Updates;
17+
18+
private array $_object = [];
19+
20+
public function certificate(int $key=0): string
21+
{
22+
return sprintf("-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----",
23+
join("\n",str_split(base64_encode(Arr::get($this->values_old,'binary.'.$key)),80))
24+
);
25+
}
26+
27+
public function cert_info(string $index,int $key=0): mixed
28+
{
29+
if (! array_key_exists($key,$this->_object))
30+
$this->_object[$key] = openssl_x509_parse(openssl_x509_read($this->certificate($key)));
31+
32+
33+
return Arr::get($this->_object[$key],$index);
34+
}
35+
36+
public function expires($key=0): Carbon
37+
{
38+
return Carbon::createFromTimestampUTC($this->cert_info('validTo_time_t',$key));
39+
}
40+
41+
public function render_item_old(string $dotkey): ?string
42+
{
43+
return join("\n",str_split(base64_encode(parent::render_item_old($dotkey)),80));
44+
}
45+
46+
public function subject($key=0): string
47+
{
48+
$subject = collect($this->cert_info('subject',$key))->reverse();
49+
50+
return $subject->map(fn($item,$key)=>sprintf("%s=%s",$key,$item))->join(',');
51+
}
52+
}

app/Ldap/Entry.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,6 @@ public function getOtherTags(): Collection
389389
fn($item)=>
390390
(! preg_match(sprintf('/^%s$/',self::TAG_NOTAG),$item))
391391
&& (! preg_match(sprintf('/^%s+$/',self::TAG_CHARS_LANG),$item))
392-
&& (! preg_match('/^binary$/',$item))
393392
)
394393
->count())
395394
)
@@ -428,9 +427,17 @@ private function getRDNObject(): Attribute\RDN
428427
*/
429428
public function getVisibleAttributes(?string $tag=NULL): Collection
430429
{
431-
return $this->objects
432-
->filter(fn($item)=>! $item->is_internal)
433-
->filter(fn($item)=>is_null($tag) || count($item->tagValues($tag)) > 0);
430+
static $cache = NULL;
431+
432+
if (is_null($cache)) {
433+
$ot = $this->getOtherTags();
434+
435+
$cache = $this->objects
436+
->filter(fn($item)=>! $item->is_internal)
437+
->filter(fn($item)=>is_null($tag) || $ot->has($item->name_lc) || count($item->tagValues($tag)) > 0);
438+
}
439+
440+
return $cache;
434441
}
435442

436443
public function hasAttribute(int|string $key): bool

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"require": {
88
"ext-fileinfo": "*",
99
"ext-ldap": "*",
10+
"ext-openssl": "*",
1011
"php": "^8.4",
1112
"directorytree/ldaprecord-laravel": "^3.0",
1213
"laravel/framework": "^11.9",
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!-- $o=UserCertificate::class -->
2+
<x-attribute.layout :edit="$edit ?? FALSE" :new="$new ?? FALSE" :o="$o" langtag="binary">
3+
@foreach($o->tagValuesOld('binary') as $key => $value)
4+
@if($edit)
5+
<input type="hidden" name="name={{ $o->name_lc }}[binary][]" value="{{ md5($value) }}">
6+
7+
<div class="input-group has-validation mb-3">
8+
<textarea class="form-control mb-1 font-monospace" rows="{{ count(explode("\n",$x=$o->certificate())) }}" style="overflow:hidden" disabled>{{ $x }}</textarea>
9+
10+
<div class="invalid-feedback pb-2">
11+
@if($e=$errors->get($o->name_lc.'.'.$langtag.'.'.$loop->index))
12+
{{ join('|',$e) }}
13+
@endif
14+
</div>
15+
</div>
16+
<div class="input-helper">
17+
@lang('Certificate Subject'): <strong>{{ $o->subject($loop->index) }}</strong><br/>
18+
{{ ($expire=$o->expires($loop->index))->isPast() ? __('Expired') : __('Expires') }}: <strong>{{ $expire->format(config('pla.datetime_format','Y-m-d H:i:s')) }}</strong>
19+
</div>
20+
21+
@else
22+
<span class="form-control mb-1"><pre class="m-0">{{ $o->render_item_old('binary.'.$key) }}</pre></span>
23+
@endif
24+
@endforeach
25+
</x-attribute.layout>

resources/views/components/attribute/widget/options.blade.php

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
@use(App\Classes\LDAP\Attribute\Binary\JpegPhoto)
22
@use(App\Classes\LDAP\Attribute\ObjectClass)
3+
@use(App\Classes\LDAP\Attribute\UserCertificate)
34
@php($clone=FALSE)
45
<span class="p-0 m-0">
56
@if($o->is_rdn)
67
<button class="btn btn-sm btn-outline-focus mt-3" disabled><i class="fas fa-fw fa-exchange"></i> @lang('Rename')</button>
78
@elseif($edit && $o->can_addvalues)
89
@switch(get_class($o))
9-
@case(JpegPhoto::class)
10-
<span @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name_lc }}" disabled><i class="fas fa-fw fa-plus"></i> @lang('Upload JpegPhoto')</span>
11-
12-
@break
13-
1410
@case(ObjectClass::class)
1511
<span type="button" @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) data-bs-toggle="modal" data-bs-target="#new_objectclass-modal"><i class="fas fa-fw fa-plus"></i> @lang('Add Objectclass')</span>
1612

@@ -216,6 +212,36 @@ function process_oc() {
216212
@append
217213
@break
218214

215+
@case(JpegPhoto::class)
216+
<span @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name }}-upload" disabled><i class="fas fa-fw fa-file-arrow-up"></i> @lang('Upload JpegPhoto')</span>
217+
@section('page-scripts')
218+
<script type="text/javascript">
219+
$(document).ready(function() {
220+
$('#{{ $o->name }}-upload.addable').click(function(e) {
221+
alert('Sorry, not implemented yet');
222+
e.preventDefault();
223+
return false;
224+
});
225+
});
226+
</script>
227+
@append
228+
@break
229+
230+
@case(UserCertificate::class)
231+
<span @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name }}-replace" disabled><i class="fas fa-fw fa-certificate"></i> @lang('Replace Certificate')</span>
232+
@section('page-scripts')
233+
<script type="text/javascript">
234+
$(document).ready(function() {
235+
$('#{{ $o->name }}-replace.addable').click(function(e) {
236+
alert('Sorry, not implemented yet');
237+
e.preventDefault();
238+
return false;
239+
});
240+
});
241+
</script>
242+
@append
243+
@break
244+
219245
<!-- All other attributes -->
220246
@default
221247
@php($clone=TRUE)

resources/views/frames/dn.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454

5555
<div class="row">
5656
<div class="col">
57-
@if(($x=$o->getOtherTags())->count())
57+
@if(($x=$o->getOtherTags()->filter(fn($item)=>$item->diff(['binary'])->count()))->count())
5858
<div class="ms-4 mt-4 alert alert-danger p-2" style="max-width: 30em; font-size: 0.80em;">
5959
This entry has [<strong>{!! $x->flatten()->join('</strong>, <strong>') !!}</strong>] tags used by [<strong>{!! $x->keys()->join('</strong>, <strong>') !!}</strong>] that cant be managed by PLA. You can though manage those tags with an LDIF import.
6060
</div>

0 commit comments

Comments
 (0)