Compare commits

..

1 Commits

Author SHA1 Message Date
1e19213566 Start of work to enable creation of new entries
All checks were successful
Create Docker Image / Test Application (x86_64) (push) Successful in 28s
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 1m33s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 4m36s
Create Docker Image / Final Docker Image Manifest (push) Successful in 10s
2025-02-23 18:14:41 +11:00
14 changed files with 50 additions and 278 deletions

View File

@ -105,7 +105,6 @@ jobs:
run: |
registry=${{ github.server_url }}
echo "registry=${registry##http*://}" >> "$GITHUB_OUTPUT"
echo "version=$(cat public/VERISON)"
- name: Container Registry Login
uses: docker/login-action@v2
@ -145,7 +144,6 @@ jobs:
- name: Record version and Delete Unnecessary files
run: |
echo Building [${{ steps.registry.outputs.version }}]
echo ${GITHUB_SHA::8} > VERSION
rm -rf .git* tests/ storage/app/test/
ls -al public/css/
@ -158,7 +156,6 @@ jobs:
file: docker/Dockerfile
push: true
tags: "${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSIONARCH }}"
build-args: BUILD_REVISION=${GITHUB_SHA},BUILD_VERSION=${{ steps.registry.outputs.version }}
manifest:
name: Final Docker Image Manifest

View File

@ -145,9 +145,9 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
// Attribute values
'values' => $this->values,
// Required by Object Classes
'required_by' => $this->schema?->required_by_object_classes ?: collect(),
'required_by' => $this->schema->required_by_object_classes,
// Used in Object Classes
'used_in' => $this->schema?->used_in_object_classes ?: collect(),
'used_in' => $this->schema->used_in_object_classes,
default => throw new \Exception('Unknown key:' . $key),
};

View File

@ -1,51 +0,0 @@
<?php
namespace App\Classes\LDAP\Attribute;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
use App\Classes\LDAP\Attribute;
/**
* Represents the RDN for an Entry
*/
final class RDN extends Attribute
{
private string $base;
private Collection $objectclasses;
public function __get(string $key): mixed
{
return match ($key) {
'base' => $this->base,
default => parent::__get($key),
};
}
public function hints(): array
{
return [
'required' => __('RDN is required')
];
}
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
{
return view('components.attribute.rdn')
->with('o',$this);
}
public function setBase(string $base): void
{
$this->base = $base;
}
public function setObjectClasses(array $classes): void
{
$this->objectclasses = collect();
foreach ($classes as $class)
$this->objectclasses->push(cofig('server')->schema('objectclasses',$class));
}
}

View File

@ -91,27 +91,20 @@ class HomeController extends Controller
*
* @param EntryAdd $request
* @return Factory|View|Application|object
* @throws InvalidUsage
*/
public function entry_add(EntryAdd $request)
{
switch ($request->step) {
case 1:
$container = Crypt::decryptString($request->dn);
$o = new Entry;
$o->objectclass = $request->objectclass;
$o->setRDNBase($container);
return view('frame')
->with('subframe',$request->frame)
->with('bases',$this->bases())
->with('o',$o)
->with('o',config('server')->fetch($x=Crypt::decryptString($request->dn)))
->with('step',$request->step)
->with('dn',$container);
->with('dn',$x);
default:
throw new InvalidUsage('Invalid entry step');
dd($request);
}
}

View File

@ -12,14 +12,11 @@ use App\Classes\LDAP\Attribute;
use App\Classes\LDAP\Attribute\Factory;
use App\Classes\LDAP\Export\LDIF;
use App\Exceptions\Import\AttributeException;
use App\Exceptions\InvalidUsage;
class Entry extends Model
{
private Collection $objects;
private bool $noObjectAttributes = FALSE;
// For new entries, this is the container that this entry will be stored in
private string $rdnbase;
/* OVERRIDES */
@ -49,7 +46,7 @@ class Entry extends Model
public function getAttributes(): array
{
return $this->objects
->map(fn($item)=>$item->values)
->map(fn($item)=>$item->values->toArray())
->toArray();
}
@ -95,7 +92,10 @@ class Entry extends Model
$key = $this->normalizeAttributeKey($key);
if ((! $this->objects->get($key)) && $value) {
$this->objects->put($key,Factory::create($key,[$value]));
$o = new Attribute($key,[]);
$o->value = $value;
$this->objects->put($key,$o);
} elseif ($this->objects->get($key)) {
$this->objects->get($key)->value = $this->attributes[$key];
@ -265,12 +265,8 @@ class Entry extends Model
*/
public function getObject(string $key): Attribute|null
{
return match ($key) {
'rdn' => $this->getRDNObject(),
default => $this->objects
->get($this->normalizeAttributeKey($key))
};
return $this->objects
->get($this->normalizeAttributeKey($key));
}
public function getObjects(): Collection
@ -293,14 +289,6 @@ class Entry extends Model
->filter(fn($a)=>(! $this->getVisibleAttributes()->contains(fn($b)=>($a->name === $b->name))));
}
private function getRDNObject(): Attribute\RDN
{
$o = new Attribute\RDN('dn',[' ']);
$o->setBase($this->rdnbase); // @todo for an existing object, return the base.
return $o;
}
/**
* Return this list of user attributes
*
@ -425,12 +413,4 @@ class Entry extends Model
return $this;
}
public function setRDNBase(string $bdn): void
{
if ($this->exists)
throw new InvalidUsage('Cannot set RDN base on existing entries');
$this->rdnbase = $bdn;
}
}

View File

@ -48,20 +48,8 @@ return [
'base_dn' => env('LDAP_BASE_DN', 'dc=local,dc=com'),
'timeout' => env('LDAP_TIMEOUT', 5),
'use_ssl' => env('LDAP_SSL', true),
'use_tls' => env('LDAP_TLS', false),
'name' => env('LDAP_NAME','LDAPS Server'),
],
'openldaptls' => [
'hosts' => [env('LDAP_HOST', '127.0.0.1')],
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
'password' => env('LDAP_PASSWORD', 'secret'),
'port' => env('LDAP_PORT', 389),
'base_dn' => env('LDAP_BASE_DN', 'dc=local,dc=com'),
'timeout' => env('LDAP_TIMEOUT', 5),
'use_ssl' => env('LDAP_SSL', false),
'use_tls' => env('LDAP_TLS', true),
'name' => env('LDAP_NAME','LDAP-TLS Server'),
'name' => env('LDAP_NAME','LDAPS Server'),
],
'opendj' => [

View File

@ -1,17 +1,5 @@
FROM dunglas/frankenphp:php8.4-alpine
ARG BUILD_VERSION=2.0.0-dev
ARG BUILD_REVISION=00000000
LABEL org.opencontainers.image.vendor="Deon George"
LABEL org.opencontainers.image.licenses=GPLv2
LABEL org.opencontainers.image.source=https://github.com/leenooks/phpldapadmin
LABEL org.opencontainers.image.title=phpLDAPadmin
LABEL org.opencontainers.image.description="An LDAP Administration Tool"
LABEL org.opencontainers.image.url=https://phpldapadmin.org
LABEL org.opencontainers.image.version=${BUILD_VERSION}
LABEL org.opencontainers.image.revision=${BUILD_REVISION}
# Base
RUN apk add --no-cache bash

View File

@ -304,7 +304,3 @@ div#objectClass .input-group-delete {
right: 10px;
height: 5px;
}
.input-group-text {
background-color: #fafafa;
}

View File

@ -4,7 +4,7 @@
<div class="col-12 col-sm-10 col-md-8">
<div class="row">
<div class="col-12 bg-light text-dark p-2">
<strong><abbr title="{{ $o->description }}">{{ $o->name }}</abbr></strong>
<strong><abbr title="{{ $o->description }}" data-attr-name="{{ $o->name_lc }}" data-attr-required="{{ $o->required_by->intersect($oc)->join('|') }}" data-oc="{{ $oc->count() ? $o->required_by->keys()->intersect($oc)->join('|') : $o->used_in->keys()->join('|') }}">{{ $o->name }}</abbr></strong>
<!-- Attribute Hints -->
<span class="float-end small">
@foreach($o->hints as $name => $description)

View File

@ -1,25 +0,0 @@
<!-- $o=RDN::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">
<x-form.select
name="rdn"
:options="[]"
/>
<span class="input-group-text">=</span>
<input type="text" @class(['form-control','is-invalid'=>($e=$errors->get($o->name_lc.'.'.$loop->index)),'border-focus'=>$o->values->contains($value)]) name="rdn" placeholder="rdn">
<label class="input-group-text" for="inputGroupSelect02">,{{ $o->base }}</label>
<div class="invalid-feedback pb-2">
@if($e)
{{ join('|',$e) }}
@endif
</div>
</div>
@else
{{ $value }}
@endif
@endforeach
</x-attribute.layout>

View File

@ -75,6 +75,10 @@
if (added_oc.sort().join('|') == newadded.sort().join('|'))
return;
var attrs = $('[data-attr-name]').map(function() {
return $(this).data('attrName');
});
// Find out what was selected, and add them
newadded.forEach(function (item) {
if (added_oc.indexOf(item) !== -1)
@ -147,6 +151,7 @@
url: '{{ url('api/schema/objectclass/attrs') }}/'+item,
cache: false
});
});
// Loop through added_oc, and remove anything not in newadded

View File

@ -52,7 +52,7 @@
theme: 'bootstrap-5',
dropdownAutoWidth: true,
width: 'style',
allowClear: {{ $allowclear ?? 'false' }},
allowClear: {{ $allowclear ?? 'true' }},
placeholder: '{{ $placeholder ?? '' }}',
multiple: {{ $multiple ?? 'false' }},
@isset($addvalues)

View File

@ -5,15 +5,10 @@
@endsection
@section('main-content')
<div class="row">
<p class="alert alert-danger text-center">
This is a tech preview of what is to come, and it is by no means complete.
</p>
</div>
<div class="row">
<div class="offset-1 col-10">
<div class="main-card mb-3 card">
<form id="create-form" action="{{ url('entry/add') }}" method="POST" enctype="multipart/form-data">
<form id="import-form" action="{{ url('entry/add') }}" method="POST" enctype="multipart/form-data">
@csrf
<input type="hidden" name="frame" value="create">
<input type="hidden" name="dn" value="{{ Crypt::encryptString($dn) }}">
@ -23,67 +18,38 @@
@lang('Create New Entry')
</div>
@switch($step)
@case(1)
<div class="card-body">
<div class="row">
<div class="col-12 col-sm-6">
<x-form.select
name="objectclass"
:label="__('Select a Structural ObjectClass...')"
:options="($oc=config('server')->schema('objectclasses'))
->filter(fn($item)=>$item->isStructural())
->sortBy(fn($item)=>$item->name_lc)
->map(fn($item,$key)=>['id'=>$key,'value'=>$item->name])"
multiple="false"
/>
</div>
@switch($step)
@case(1)
<div class="card-body">
<div class="row">
<div class="col-12 col-sm-6">
<x-form.select
name="objectclass"
:label="__('Select a Structural Object Class...')"
:options="($oc=config('server')->schema('objectclasses'))
->filter(fn($item)=>$item->isStructural())
->sortBy(fn($item)=>$item->name_lc)
->map(fn($item,$key)=>['id'=>$key,'value'=>$item->name])"
allowclear="false"
multiple="false"
/>
</div>
</div>
@break
</div>
@break
@case(2)
<div class="card-body">
<x-attribute-type :edit="true" :o="$o->getObject('rdn')"/>
@foreach ($o->getVisibleAttributes() as $ao)
<x-attribute-type :edit="true" :o="$ao"/>
@endforeach
<div id="newattrs"></div>
<!-- Add new attributes -->
<div class="row">
<div class="col-12 col-sm-1 col-md-2"></div>
<div class="col-12 col-sm-10 col-md-8">
<div class="d-none" id="newattr-select">
@if($o->getMissingAttributes()->count())
<div class="row">
<div class="col-12 bg-dark text-light p-2">
<i class="fas fa-plus-circle"></i> Add New Attribute
</div>
</div>
<div class="row">
<div class="col-12 pt-2">
<x-form.select id="newattr" label="Select from..." :options="$o->getMissingAttributes()->sortBy('name')->map(fn($item)=>['id'=>$item->name,'value'=>$item->name_lc])"/>
</div>
</div>
@endif
</div>
</div>
<div class="col-2"></div>
</div>
<div class="row pt-3">
<div class="col-12 offset-sm-2 col-sm-4 col-lg-2">
<x-form.reset form="dn-add"/>
<x-form.submit action="Create" form="dn-add"/>
</div>
@case(2)
<div class="card-body">
<div class="row">
<div class="col-12 col-sm-6">
<p class="alert alert-danger">
Not ready yet :)
</p>
</div>
</div>
@break;
</div>
@break;
@endswitch
<div class="card-footer">
@ -96,68 +62,3 @@
</div>
</div>
@endsection
@section('page-scripts')
<script type="text/javascript">
var dn = '{{ $o->getDNSecure() }}';
var oc = {!! $o->getObject('objectclass')->values !!};
function editmode() {
$('#dn-edit input[name="dn"]').val(dn);
$('button[id=entry-edit]').addClass('active').removeClass('btn-outline-dark').addClass('btn-outline-light');
// Find all input items and turn off readonly
$('input.form-control').each(function() {
// Except for objectClass - @todo show an "X" instead
if ($(this)[0].name.match(/^objectclass/))
return;
$(this).attr('readonly',false);
});
// Our password type
$('div#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');
@if($o->getMissingAttributes()->count())
$('#newattr-select.d-none').removeClass('d-none');
@endif
}
$(document).ready(function() {
$('#newattr').on('change',function(item) {
$.ajax({
type: 'POST',
beforeSend: function() {},
success: function(data) {
$('#newattrs').append(data);
},
error: function(e) {
if (e.status != 412)
alert('That didnt work? Please try again....');
},
url: '{{ url('entry/attr/add') }}/'+item.target.value,
data: {
objectclasses: oc,
},
cache: false
});
// Remove the option from the list
$(this).find('[value="'+item.target.value+'"]').remove()
// If there are no more options
if ($(this).find("option").length === 1)
$('#newattr-select').remove();
});
editmode();
});
</script>
@append

View File

@ -31,7 +31,7 @@
<input type="hidden" name="dn" value="">
@foreach ($o->getVisibleAttributes() as $ao)
<x-attribute-type :edit="true" :o="$ao"/>
<x-attribute-type :edit="true" :o="$ao" :oc="collect($o->objectclass)"/>
@endforeach
<div id="newattrs"></div>