• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daycry / auth / 22527658769

28 Feb 2026 07:41PM UTC coverage: 63.267% (-3.6%) from 66.864%
22527658769

push

github

web-flow
Merge pull request #36 from daycry/development

Implement TOTP 2FA, JWT auth, device session tracking, and docs overhaul

465 of 1168 new or added lines in 52 files covered. (39.81%)

129 existing lines in 46 files now uncovered.

3064 of 4843 relevant lines covered (63.27%)

41.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Controllers/Admin/UsersController.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of Daycry Auth.
7
 *
8
 * (c) Daycry <daycry9@proton.me>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace Daycry\Auth\Controllers\Admin;
15

16
use CodeIgniter\HTTP\RedirectResponse;
17
use Daycry\Auth\Entities\User;
18
use Daycry\Auth\Exceptions\AuthorizationException;
19
use Daycry\Auth\Models\DeviceSessionModel;
20
use Daycry\Auth\Models\GroupModel;
21
use Daycry\Auth\Models\PermissionModel;
22
use Daycry\Auth\Models\UserModel;
23

24
/**
25
 * Admin Users Controller — full CRUD for user accounts.
26
 */
27
class UsersController extends BaseAdminController
28
{
29
    /**
30
     * Paginated list of users with optional keyword search.
31
     */
NEW
32
    public function index(): string
×
33
    {
34
        /** @var UserModel $userModel */
NEW
35
        $userModel = model(UserModel::class);
×
36

NEW
37
        $search = (string) $this->request->getGet('q');
×
38

NEW
39
        if ($search !== '') {
×
NEW
40
            $userModel->groupStart()
×
NEW
41
                ->like('username', $search)
×
NEW
42
                ->orLike('status', $search)
×
NEW
43
                ->groupEnd();
×
44
        }
45

NEW
46
        $users = $userModel
×
NEW
47
            ->orderBy('id', 'DESC')
×
NEW
48
            ->paginate(20, 'default');
×
49

NEW
50
        return $this->view('Daycry\\Auth\\Views\\admin\\users\\index', [
×
NEW
51
            'users'  => $users,
×
NEW
52
            'pager'  => $userModel->pager,
×
NEW
53
            'search' => $search,
×
NEW
54
        ]);
×
55
    }
56

57
    /**
58
     * Show a single user's detail: info, groups, permissions, device sessions.
59
     *
60
     * @param int|string $id
61
     */
NEW
62
    public function show($id): RedirectResponse|string
×
63
    {
NEW
64
        $user = $this->findUserOr404((int) $id);
×
65

NEW
66
        if ($user instanceof RedirectResponse) {
×
NEW
67
            return $user;
×
68
        }
69

70
        /** @var DeviceSessionModel $deviceModel */
NEW
71
        $deviceModel = model(DeviceSessionModel::class);
×
72

NEW
73
        return $this->view('Daycry\\Auth\\Views\\admin\\users\\show', [
×
NEW
74
            'user'     => $user,
×
NEW
75
            'sessions' => $deviceModel->getAllForUser($user),
×
NEW
76
        ]);
×
77
    }
78

79
    /**
80
     * Show the edit form for a user.
81
     *
82
     * @param int|string $id
83
     */
NEW
84
    public function edit($id): RedirectResponse|string
×
85
    {
NEW
86
        $user = $this->findUserOr404((int) $id);
×
87

NEW
88
        if ($user instanceof RedirectResponse) {
×
NEW
89
            return $user;
×
90
        }
91

92
        /** @var GroupModel $groupModel */
NEW
93
        $groupModel = model(GroupModel::class);
×
94

95
        /** @var PermissionModel $permissionModel */
NEW
96
        $permissionModel = model(PermissionModel::class);
×
97

NEW
98
        return $this->view('Daycry\\Auth\\Views\\admin\\users\\edit', [
×
NEW
99
            'user'      => $user,
×
NEW
100
            'allGroups' => $groupModel->findAll(),
×
NEW
101
            'allPerms'  => $permissionModel->findAll(),
×
NEW
102
        ]);
×
103
    }
104

105
    /**
106
     * Process the edit form.
107
     *
108
     * @param int|string $id
109
     */
NEW
110
    public function update($id): RedirectResponse
×
111
    {
NEW
112
        $user = $this->findUserOr404((int) $id);
×
113

NEW
114
        if ($user instanceof RedirectResponse) {
×
NEW
115
            return $user;
×
116
        }
117

118
        /** @var UserModel $userModel */
NEW
119
        $userModel = model(UserModel::class);
×
120

NEW
121
        $username = trim((string) $this->request->getPost('username'));
×
NEW
122
        $email    = trim((string) $this->request->getPost('email'));
×
NEW
123
        $active   = (bool) $this->request->getPost('active');
×
124

NEW
125
        if ($username !== '') {
×
NEW
126
            $user->username = $username;
×
127
        }
128

NEW
129
        if ($email !== '') {
×
NEW
130
            $user->email = $email;
×
131
        }
132

NEW
133
        $user->active = $active;
×
134

NEW
135
        $userModel->save($user);
×
136

137
        try {
138
            // Sync groups
NEW
139
            $newGroups = array_filter((array) $this->request->getPost('groups'));
×
NEW
140
            $user->syncGroups(...$newGroups);
×
141

142
            // Sync permissions
NEW
143
            $newPerms = array_filter((array) $this->request->getPost('permissions'));
×
NEW
144
            $user->syncPermissions(...$newPerms);
×
NEW
145
        } catch (AuthorizationException $e) {
×
NEW
146
            return redirect()->route('admin-user-edit', [$id])
×
NEW
147
                ->with('error', $e->getMessage());
×
148
        }
149

NEW
150
        return redirect()->route('admin-user-show', [$id])
×
NEW
151
            ->with('message', 'User updated successfully.');
×
152
    }
153

154
    /**
155
     * Ban a user.
156
     *
157
     * @param int|string $id
158
     */
NEW
159
    public function ban($id): RedirectResponse
×
160
    {
NEW
161
        $user = $this->findUserOr404((int) $id);
×
162

NEW
163
        if ($user instanceof RedirectResponse) {
×
NEW
164
            return $user;
×
165
        }
166

NEW
167
        $user->ban((string) $this->request->getPost('reason'));
×
168

NEW
169
        return redirect()->route('admin-user-show', [$id])
×
NEW
170
            ->with('message', 'User has been banned.');
×
171
    }
172

173
    /**
174
     * Remove ban from a user.
175
     *
176
     * @param int|string $id
177
     */
NEW
178
    public function unban($id): RedirectResponse
×
179
    {
NEW
180
        $user = $this->findUserOr404((int) $id);
×
181

NEW
182
        if ($user instanceof RedirectResponse) {
×
NEW
183
            return $user;
×
184
        }
185

NEW
186
        $user->unBan();
×
187

NEW
188
        return redirect()->route('admin-user-show', [$id])
×
NEW
189
            ->with('message', 'User has been unbanned.');
×
190
    }
191

192
    /**
193
     * Manually activate a user.
194
     *
195
     * @param int|string $id
196
     */
NEW
197
    public function activate($id): RedirectResponse
×
198
    {
NEW
199
        $user = $this->findUserOr404((int) $id);
×
200

NEW
201
        if ($user instanceof RedirectResponse) {
×
NEW
202
            return $user;
×
203
        }
204

205
        /** @var UserModel $userModel */
NEW
206
        $userModel = model(UserModel::class);
×
NEW
207
        $userModel->activate($user);
×
208

NEW
209
        return redirect()->route('admin-user-show', [$id])
×
NEW
210
            ->with('message', 'User has been activated.');
×
211
    }
212

213
    /**
214
     * Delete a user (hard delete).
215
     *
216
     * @param int|string $id
217
     */
NEW
218
    public function delete($id): RedirectResponse
×
219
    {
NEW
220
        $user = $this->findUserOr404((int) $id);
×
221

NEW
222
        if ($user instanceof RedirectResponse) {
×
NEW
223
            return $user;
×
224
        }
225

226
        /** @var UserModel $userModel */
NEW
227
        $userModel = model(UserModel::class);
×
NEW
228
        $userModel->delete($user->id, true);
×
229

NEW
230
        return redirect()->route('admin-users')
×
NEW
231
            ->with('message', 'User deleted permanently.');
×
232
    }
233

234
    // ── Private helpers ───────────────────────────────────────────
235

236
    /**
237
     * Find a user by ID or redirect with error.
238
     */
NEW
239
    private function findUserOr404(int $id): RedirectResponse|User
×
240
    {
241
        /** @var UserModel $userModel */
NEW
242
        $userModel = model(UserModel::class);
×
NEW
243
        $user      = $userModel->withIdentities()->findById($id);
×
244

NEW
245
        if ($user === null) {
×
NEW
246
            return redirect()->route('admin-users')->with('error', 'User not found.');
×
247
        }
248

NEW
249
        return $user;
×
250
    }
251
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc