From 83948c6c43b7d950fbce41efdea969afc5e47d41 Mon Sep 17 00:00:00 2001 From: Marc Schlagenhauf Date: Thu, 24 Jul 2025 02:31:01 +0200 Subject: [PATCH 1/2] Save relation between groups and roles in database and sync group members to LDAP by script --- app/Console/Commands/LdapSyncRoles.php | 41 ++++++++++++++++++- app/Livewire/Group/AddRoleToGroup.php | 11 +++-- app/Livewire/Group/ListRolesInGroup.php | 28 ++++--------- app/Models/GroupMembership.php | 37 +++++++++++++++++ ...6_27_095320_create_role_group_relation.php | 29 +++++++++++++ 5 files changed, 123 insertions(+), 23 deletions(-) create mode 100644 app/Models/GroupMembership.php create mode 100644 database/migrations/2025_06_27_095320_create_role_group_relation.php diff --git a/app/Console/Commands/LdapSyncRoles.php b/app/Console/Commands/LdapSyncRoles.php index c7e984e..1898c5f 100644 --- a/app/Console/Commands/LdapSyncRoles.php +++ b/app/Console/Commands/LdapSyncRoles.php @@ -4,11 +4,14 @@ use App\Ldap\Committee; use App\Ldap\Community; +use App\Ldap\Group; use App\Ldap\Role; +use App\Models\GroupMembership; use App\Models\RoleMembership; use Carbon\Carbon; use Illuminate\Console\Command; use Illuminate\Contracts\Console\Isolatable; +use Illuminate\Support\Facades\DB; class LdapSyncRoles extends Command { @@ -45,6 +48,9 @@ public function handle() ->setDn(Community::$rootDn) ->search('ou', $this->argument('community')) ->get(); + + $this->comment("Committees:"); + foreach ($realms as $realm){ $committees = Committee::fromCommunity($realm->getFirstAttribute('ou')) ->search('ou', $this->argument('committee')) @@ -60,7 +66,7 @@ public function handle() ->where('committee_dn', $committee->getDn()) ->where('role_cn', $role->getFirstAttribute('cn')) ->get(); - $this->comment(" |-> " .$role->getDn()); + $this->comment(" |-> " . $role->getDn()); // delete all members so far $role->setAttribute('uniqueMember', ['']); $ldapMembers = $role->members(); @@ -73,5 +79,38 @@ public function handle() } } } + + $this->comment("\nGroups:"); + + foreach ($realms as $realm) { + $groups = Group::fromCommunity($realm->getFirstAttribute('ou')) + ->search('ou', $this->argument('group')) + ->get(); + + foreach ($groups as $group) { + $this->comment("> " . $group->getDn()); + + // delete all members so far + $group->setAttribute('uniqueMember', ['']); + + $roles = GroupMembership::where('group_dn', $group->getDn())->get(); + + foreach ($roles as $role) { + $roleCn = str_replace('cn=', '', substr($role->role_dn, 0, strpos($role->role_dn, ','))); + $committeeDn = strstr($role->role_dn, "ou="); + $activeMemberships = RoleMembership::active($date) + ->where('committee_dn', $committeeDn) + ->where('role_cn', $roleCn) + ->get(); + + $ldapMembers = $group->users(); + foreach ($activeMemberships as $membership) { + // add only active members back + $this->comment(" |-> $membership->username"); + $ldapMembers->attach($membership->user->ldap()); + } + } + } + } } } diff --git a/app/Livewire/Group/AddRoleToGroup.php b/app/Livewire/Group/AddRoleToGroup.php index 78e2045..6974567 100644 --- a/app/Livewire/Group/AddRoleToGroup.php +++ b/app/Livewire/Group/AddRoleToGroup.php @@ -9,6 +9,8 @@ use Livewire\Attributes\Locked; use Livewire\Component; +use App\Models\GroupMembership; + class AddRoleToGroup extends Component { #[Locked] @@ -42,9 +44,12 @@ public function render() public function save() { - /** @var Group $group */ - $group = Group::findOrFail(Group::dnFrom($this->uid, $this->group_cn)); - $group->members()->attach($this->selected_role_dn); + $group_dn = Group::findOrFail(Group::dnFrom($this->uid, $this->group_cn)); + GroupMembership::create([ + 'group_dn' => $group_dn, + 'role_dn' => $this->selected_role_dn, + ]); + return redirect()->route('realms.groups.roles', ['uid' => $this->uid, 'cn' => $this->group_cn]) ->with('message', __('groups.success_role_add')) ; diff --git a/app/Livewire/Group/ListRolesInGroup.php b/app/Livewire/Group/ListRolesInGroup.php index d21b0dc..a38c127 100644 --- a/app/Livewire/Group/ListRolesInGroup.php +++ b/app/Livewire/Group/ListRolesInGroup.php @@ -5,6 +5,7 @@ use App\Ldap\Community; use App\Ldap\Group; use App\Ldap\Role; +use App\Models\GroupMembership; use Livewire\Attributes\Url; use Livewire\Component; use Livewire\WithPagination; @@ -51,16 +52,15 @@ public function updatedSearch(): void } public function render() { - /** @var Group $group */ - $group = Group::findOrFail($this->group_dn); - $roles = $group->members()->get(); - $users = $group->users()->get(); - // slice breaks it, whyever - get to go. + $rolesDB = GroupMembership::select('role_dn')->where('group_dn', $this->group_dn)->get(); + $roles = []; + foreach ($rolesDB as $row) { + $role = Role::findOrFail($row->role_dn); + array_push($roles, $role); + } return view( 'livewire.group.roles', [ 'roles' => $roles, - 'users' => $users, - 'group' => $group, ] )->title(__('groups.roles_list_title', ['name' => $this->group_cn])); } @@ -75,14 +75,7 @@ public function deletePrepare(string $role_dn): void $committee = $role->committee(); $this->deleteRoleDN = $role_dn; - $this->deleteRoleName = [ - 'role_short' => $role->getFirstAttribute('description'), - 'role_name' => $role->getFirstAttribute('cn'), - 'committee_name' => $committee?->getFirstAttribute('description'), - 'committee_short' => $committee?->getFirstAttribute('ou'), - 'group_short' => $group->getFirstAttribute('cn'), - 'group_name' => $group->getFirstAttribute('description') - ]; + $this->deleteRoleName = [ $role->getFirstAttribute('cn') ]; $this->showDeleteModal = true; } @@ -92,10 +85,7 @@ public function deleteCommit(): void $community = Community::findByUid($this->realm_uid); $this->authorize('delete', [Group::class, $community]); - $group = Group::findOrFail($this->group_dn); - $role = Role::findOrFail($this->deleteRoleDN); - - $group->roles()->detach($role); + GroupMembership::where('group_dn', $this->group_dn)->where('role_dn', $this->deleteRoleDN)->delete(); $this->close(); } diff --git a/app/Models/GroupMembership.php b/app/Models/GroupMembership.php new file mode 100644 index 0000000..1e0cb05 --- /dev/null +++ b/app/Models/GroupMembership.php @@ -0,0 +1,37 @@ +hasMany(Role::class); + } +} diff --git a/database/migrations/2025_06_27_095320_create_role_group_relation.php b/database/migrations/2025_06_27_095320_create_role_group_relation.php new file mode 100644 index 0000000..781b42d --- /dev/null +++ b/database/migrations/2025_06_27_095320_create_role_group_relation.php @@ -0,0 +1,29 @@ +id(); + $table->string('group_dn'); + $table->string('role_dn'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('role_group_relation'); + } +}; From 10de16eb33adc3e67a896e8e1b8cd303e968b4c6 Mon Sep 17 00:00:00 2001 From: Marc Schlagenhauf Date: Wed, 26 Nov 2025 03:49:23 +0100 Subject: [PATCH 2/2] Script to move the roles of all groups from LDAP to the database --- .../MoveGroupRolesFromLdapToDatabase.php | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 app/Console/Commands/MoveGroupRolesFromLdapToDatabase.php diff --git a/app/Console/Commands/MoveGroupRolesFromLdapToDatabase.php b/app/Console/Commands/MoveGroupRolesFromLdapToDatabase.php new file mode 100644 index 0000000..3f1d31b --- /dev/null +++ b/app/Console/Commands/MoveGroupRolesFromLdapToDatabase.php @@ -0,0 +1,60 @@ +list() // only first level + ->setDn(Community::$rootDn) + ->search('ou', $this->argument('community')) + ->get(); + + foreach ($realms as $realm) { + $groups = Group::fromCommunity($realm->getFirstAttribute('ou')) + ->search('ou', $this->argument('group')) + ->get(); + + foreach ($groups as $group) { + $this->comment("> " . $group->getDn()); + + // get roles + $roles = $group->members()->get(); + + foreach ($roles as $role) { + $this->comment($role); + GroupMembership::create([ + 'group_dn' => $group->getDn(), + 'role_dn' => $role, + ]); + } + } + } + } +}