Compare commits

...

11 Commits

Author SHA1 Message Date
af7ca851d5 Release v2.0.2
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 1m40s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 4m33s
Create Docker Image / Final Docker Image Manifest (push) Successful in 9s
2025-03-11 20:11:36 +11:00
b34dad8836 Fix when adding a new objectclass with required attributes, validation errors are correctly display on the returned form 2025-03-10 13:25:43 +11:00
ef2ea5e266 Fix detection of new attributes added to an entry 2025-03-10 13:25:43 +11:00
91b5b53137 When making new attributes available, only render unique attributes 2025-03-10 13:25:43 +11:00
d4c916923d When adding new attributes as a result of adding a new objectclass, dont duplicate existing attributes already present 2025-03-10 12:35:38 +11:00
e94a7d58e1 Disable buttons that we havent implemented yet, update README with some more todos 2025-03-09 14:08:45 +11:00
15d5bf605a Include loopback in our trusted proxies configuration - fixes #294 2025-03-09 13:32:58 +11:00
33c59e5e65 Release v2.0.1
All checks were successful
Create Docker Image / Test Application (x86_64) (push) Successful in 27s
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 1m23s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 4m30s
Create Docker Image / Final Docker Image Manifest (push) Successful in 9s
2025-03-07 16:56:03 +11:00
c86d3c97a1 CSS fix to remove border around logged in user icon 2025-03-07 16:56:03 +11:00
be87a12f21 We need to start the application after we've swapped the user details from the cookie, otherwise $user is initialised by the LDAP_USERNAME credentials - which may not have access to all the attributes 2025-03-07 16:36:35 +11:00
e99e349c0b Make the file-note responsive to screen size, with a more appropriate size 2025-03-07 13:32:09 +11:00
15 changed files with 93 additions and 68 deletions

View File

@ -46,6 +46,7 @@ The update to v2 is progressing well - here is a list of work to do and done:
- [X] Delete extra values for Attributes that support multiple values - [X] Delete extra values for Attributes that support multiple values
- [ ] Delete Attributes - [ ] Delete Attributes
- [ ] Templates to enable entries to conform to a custom standard - [ ] Templates to enable entries to conform to a custom standard
- [ ] Autopopulate attribute values
- [X] Login to LDAP server - [X] Login to LDAP server
- [X] Configure login by a specific attribute - [X] Configure login by a specific attribute
- [X] Logout LDAP server - [X] Logout LDAP server
@ -53,6 +54,7 @@ The update to v2 is progressing well - here is a list of work to do and done:
- [X] Import LDIF - [X] Import LDIF
- [X] Schema Browser - [X] Schema Browser
- [ ] Searching - [ ] Searching
- [ ] Enforcing attribute uniqueness
- [ ] Is there something missing? - [ ] Is there something missing?
Support is known for these LDAP servers: Support is known for these LDAP servers:

View File

@ -383,6 +383,11 @@ class HomeController extends Controller
: view('frames.'.$key['cmd'])) : view('frames.'.$key['cmd']))
->with('bases',$this->bases()); ->with('bases',$this->bases());
// If we are rendering a DN, rebuild our object
$o = config('server')->fetch($key['dn']);
foreach (collect(old())->except(['dn','_token']) as $attr => $value)
$o->{$attr} = $value;
return match ($key['cmd']) { return match ($key['cmd']) {
'create' => $view 'create' => $view
->with('container',old('container',$key['dn'])) ->with('container',old('container',$key['dn']))
@ -390,7 +395,8 @@ class HomeController extends Controller
'dn' => $view 'dn' => $view
->with('dn',$key['dn']) ->with('dn',$key['dn'])
->with('page_actions',collect(['edit'=>TRUE,'copy'=>TRUE])), ->with('o',$o)
->with('page_actions',collect(['edit'=>TRUE])),
'import' => $view, 'import' => $view,

View File

@ -94,12 +94,10 @@ class Entry extends Model
$key = $this->normalizeAttributeKey($key); $key = $this->normalizeAttributeKey($key);
if ((! $this->objects->get($key)) && $value) { if ((! $this->objects->get($key)) && $value)
$this->objects->put($key,Factory::create($key,$value)); $this->objects->put($key,Factory::create($key,[]));
} elseif ($this->objects->get($key)) {
$this->objects->get($key)->value = $this->attributes[$key]; $this->objects->get($key)->value = $this->attributes[$key];
}
return $this; return $this;
} }
@ -164,7 +162,6 @@ class Entry extends Model
/** /**
* Convert all our attribute values into an array of Objects * Convert all our attribute values into an array of Objects
* *
* @param array $attributes
* @return Collection * @return Collection
*/ */
public function getAttributesAsObjects(): Collection public function getAttributesAsObjects(): Collection

View File

@ -16,20 +16,21 @@ return Application::configure(basePath: dirname(__DIR__))
) )
->withMiddleware(function (Middleware $middleware) { ->withMiddleware(function (Middleware $middleware) {
$middleware->appendToGroup('web', [ $middleware->appendToGroup('web', [
ApplicationSession::class,
SwapinAuthUser::class, SwapinAuthUser::class,
ApplicationSession::class,
CheckUpdate::class, CheckUpdate::class,
]); ]);
$middleware->prependToGroup('api', [ $middleware->prependToGroup('api', [
EncryptCookies::class, EncryptCookies::class,
ApplicationSession::class,
SwapinAuthUser::class, SwapinAuthUser::class,
ApplicationSession::class,
AllowAnonymous::class, AllowAnonymous::class,
]); ]);
$middleware->trustProxies(at: [ $middleware->trustProxies(at: [
'10.0.0.0/8', '10.0.0.0/8',
'127.0.0.0/8',
'172.16.0.0/12', '172.16.0.0/12',
'192.168.0.0/12', '192.168.0.0/12',
]); ]);

View File

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

View File

@ -1,5 +1,5 @@
/** ensure our userpassword has select is next to the password input */ /** ensure our userpassword has select is next to the password input */
div#userPassword .select2-container--bootstrap-5 .select2-selection { attribute#userPassword .select2-container--bootstrap-5 .select2-selection {
font-size: inherit; font-size: inherit;
width: 9em; width: 9em;
border: #444054 1px solid; border: #444054 1px solid;
@ -11,7 +11,7 @@ div#userPassword .select2-container--bootstrap-5 .select2-selection {
border-top-right-radius: unset; border-top-right-radius: unset;
} }
div#objectClass .input-group-end:not(input.form-control) { attribute#objectClass .input-group-end:not(input.form-control) {
position: absolute; position: absolute;
right: 1em; right: 1em;
top: 0.5em; top: 0.5em;

View File

@ -248,3 +248,8 @@ select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__
.input-group-text { .input-group-text {
background-color: #fafafa; background-color: #fafafa;
} }
/* Stop showing a border on our user's drop down menu when open */
.btn-check:checked+.btn, .btn.active, .btn.show, .btn:first-child:active, :not(.btn-check)+.btn:active {
border-color: var(--bs-btn-bg);
}

View File

@ -27,7 +27,7 @@
@endif @endif
@if(isset($page_actions) && $page_actions->contains('copy')) @if(isset($page_actions) && $page_actions->contains('copy'))
<li> <li>
<button class="btn btn-outline-dark p-1 m-1" id="entry-copy-move" data-bs-toggle="tooltip" data-bs-placement="bottom" title="@lang('Copy/Move')"><i class="fas fa-fw fa-copy fs-5"></i></button> <button class="btn btn-outline-dark p-1 m-1" id="entry-copy-move" data-bs-toggle="tooltip" data-bs-placement="bottom" title="@lang('Copy/Move')" disabled><i class="fas fa-fw fa-copy fs-5"></i></button>
</li> </li>
@endif @endif
@if((isset($page_actions) && $page_actions->contains('edit')) || old()) @if((isset($page_actions) && $page_actions->contains('edit')) || old())

View File

@ -124,7 +124,7 @@
--}} --}}
<div class="widget-content-left header-user-info ms-3"> <div class="widget-content-left header-user-info ms-3">
<div class="widget-heading"> <div class="widget-heading">
{{ $user->exists ? Arr::get($user->getAttribute('cn'),0,'Anonymous') : 'Anonymous' }} {{ $user->exists ? Arr::get($user->getAttribute('cn'),0,Arr::get($user->getAttribute('entryuuid'),0,'Secret Person')) : 'Anonymous' }}
</div> </div>
<div class="widget-subheading"> <div class="widget-subheading">
{{ $user->exists ? Arr::get($user->getAttribute('mail'),0,'') : '' }} {{ $user->exists ? Arr::get($user->getAttribute('mail'),0,'') : '' }}

View File

@ -14,7 +14,7 @@
@if ($edit) @if ($edit)
<br> <br>
<!-- @todo TO IMPLEMENT --> <!-- @todo TO IMPLEMENT -->
<span class="btn btn-sm btn-danger deletable d-none mt-3"><i class="fas fa-trash-alt"></i> @lang('Delete')</span> <button class="btn btn-sm btn-danger deletable d-none mt-3" disabled><i class="fas fa-trash-alt"></i> @lang('Delete')</button>
<div class="invalid-feedback pb-2"> <div class="invalid-feedback pb-2">
@if($e) @if($e)

View File

@ -1,9 +1,9 @@
<div class="row pt-2"> <div class="row pt-2">
<div @class(['col-1','d-none'=>(! $edit)])></div> <div @class(['col-1','d-none'=>(! $edit)])></div>
<div class="col-10 p-2"> <div class="col-10 p-2">
<div id="{{ $o->name }}"> <attribute id="{{ $o->name }}">
{{ $slot }} {{ $slot }}
</div> </attribute>
<x-attribute.widget.options :o="$o" :edit="$edit" :new="$new"/> <x-attribute.widget.options :o="$o" :edit="$edit" :new="$new"/>
</div> </div>

View File

@ -1,15 +1,18 @@
@use(App\Classes\LDAP\Attribute\Binary\JpegPhoto)
@use(App\Classes\LDAP\Attribute\ObjectClass)
@php($clone=FALSE) @php($clone=FALSE)
@if($o->is_rdn) <span class="p-0 m-0">
<span class="btn btn-sm btn-outline-focus mt-3"><i class="fas fa-fw fa-exchange"></i> @lang('Rename')</span> @if($o->is_rdn)
@elseif($edit && $o->can_addvalues) <br/>
<span class="p-0 m-0"> <button class="btn btn-sm btn-outline-focus mt-3" disabled><i class="fas fa-fw fa-exchange"></i> @lang('Rename')</button>
@elseif($edit && $o->can_addvalues)
@switch(get_class($o)) @switch(get_class($o))
@case('App\Classes\LDAP\Attribute\Binary\JpegPhoto') @case(JpegPhoto::class)
<span @class(['btn','btn-sm','btn-outline-primary','mt-3','addable','d-none'=>(! $new)]) id="{{ $o->name_lc }}"><i class="fas fa-fw fa-plus"></i> @lang('Upload JpegPhoto')</span> <button @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')</button>
@break @break
@case('App\Classes\LDAP\Attribute\ObjectClass') @case(ObjectClass::class)
<button 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')</button> <button 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')</button>
<!-- NEW OBJECT CLASS --> <!-- NEW OBJECT CLASS -->
@ -37,42 +40,12 @@
$(document).ready(function() { $(document).ready(function() {
var added_oc = []; // Object classes being added to this entry var added_oc = []; // Object classes being added to this entry
var rendered = false; var rendered = false;
var newadded = [];
// Show our ObjectClass modal so that we can add more objectclasses if (newadded.length)
$('#new_objectclass-modal').on('shown.bs.modal',function() { process_oc();
if (! rendered)
$.ajax({
method: 'POST',
url: '{{ url('entry/objectclass/add') }}',
data: {
oc: oc,
},
cache: false,
success: function(data) {
$('select#newoc').select2({
dropdownParent: $('#new_objectclass-modal'),
theme: 'bootstrap-5',
multiple: true,
data: data,
});
},
error: function(e) {
if (e.status !== 412)
alert('That didnt work? Please try again....');
},
});
rendered = true;
})
// When the ObjectClass modal is closed, process what was selected
$('#new_objectclass-modal').on('hide.bs.modal',function() {
var newadded = $('select#newoc').val();
// If nothing selected, we dont have anything to do
if (added_oc.sort().join('|') === newadded.sort().join('|'))
return;
function process_oc() {
// Find out what was selected, and add them // Find out what was selected, and add them
newadded.forEach(function (item) { newadded.forEach(function (item) {
if (added_oc.indexOf(item) !== -1) if (added_oc.indexOf(item) !== -1)
@ -97,6 +70,7 @@
}, },
}); });
// Get a list of attributes already on the page, so we dont double up
$.ajax({ $.ajax({
method: 'POST', method: 'POST',
url: '{{ url('api/schema/objectclass/attrs') }}/'+item, url: '{{ url('api/schema/objectclass/attrs') }}/'+item,
@ -105,6 +79,9 @@
// Render any must attributes // Render any must attributes
if (data.must.length) { if (data.must.length) {
data.must.forEach(function(item) { data.must.forEach(function(item) {
if ($('attribute#'+item).length)
return;
// Add attribute to the page // Add attribute to the page
$.ajax({ $.ajax({
method: 'POST', method: 'POST',
@ -196,13 +173,51 @@
}); });
added_oc = newadded; added_oc = newadded;
}
// Show our ObjectClass modal so that we can add more objectclasses
$('#new_objectclass-modal').on('shown.bs.modal',function() {
if (! rendered)
$.ajax({
method: 'POST',
url: '{{ url('entry/objectclass/add') }}',
data: {
oc: oc,
},
cache: false,
success: function(data) {
$('select#newoc').select2({
dropdownParent: $('#new_objectclass-modal'),
theme: 'bootstrap-5',
multiple: true,
data: data,
});
},
error: function(e) {
if (e.status !== 412)
alert('That didnt work? Please try again....');
},
});
rendered = true;
})
// When the ObjectClass modal is closed, process what was selected
$('#new_objectclass-modal').on('hide.bs.modal',function() {
newadded = $('select#newoc').val();
// If nothing selected, we dont have anything to do
if (added_oc.sort().join('|') === newadded.sort().join('|'))
return;
process_oc();
}); });
}); });
</script> </script>
@append @append
@break @break
@case('App\Classes\LDAP\Attribute') <!-- All other attributes -->
@default @default
@php($clone=TRUE) @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> <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>
@ -222,5 +237,5 @@
@endif @endif
@append @append
@endswitch @endswitch
</span> @endif
@endif </span>

View File

@ -1,7 +1,7 @@
@if(file_exists($file)) @if(file_exists($file))
<div class="row pb-3"> <div class="row pb-3">
<div class="col-12"> <div class="col-12 offset-md-2 col-md-8">
<div class="mx-auto card text-white card-body bg-primary w-50"> <div class="mx-auto card text-white card-body bg-primary">
<h5 class="text-white card-title"><i class="icon fa-2x fas fa-info pe-3"></i><span class="font-size-xlg">NOTE</span></h5> <h5 class="text-white card-title"><i class="icon fa-2x fas fa-info pe-3"></i><span class="font-size-xlg">NOTE</span></h5>
<span class="w-100 pb-0"> <span class="w-100 pb-0">
{!! file_get_contents($file) !!} {!! file_get_contents($file) !!}

View File

@ -13,10 +13,9 @@
<div class="row"> <div class="row">
<div class="col-12 pt-2"> <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])"/> <x-form.select id="newattr" label="Select from..." :options="$o->getMissingAttributes()->sortBy('name')->unique('name')->map(fn($item)=>['id'=>$item->name,'value'=>$item->name_lc])"/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-2"></div>
</div> </div>

View File

@ -1,7 +1,7 @@
@extends('layouts.dn') @extends('layouts.dn')
@section('page_title') @section('page_title')
@include('fragment.dn.header',['o'=>($o=config('server')->fetch($dn))]) @include('fragment.dn.header',['o'=>($o ?? $o=config('server')->fetch($dn))])
@endsection @endsection
@section('main-content') @section('main-content')