devsofttech_hms_ci4/app/Controllers/Setting/UserManagement.php

499 lines
16 KiB
PHP

<?php
namespace App\Controllers\Setting;
use App\Controllers\BaseController;
use CodeIgniter\Shield\Entities\User;
use CodeIgniter\Shield\Models\UserModel;
class UserManagement extends BaseController
{
public function index(): string
{
$userModel = model(UserModel::class);
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
]);
}
public function create(): string
{
return view('Setting/Admin/UserManagement/create', [
'roles' => setting('AuthGroups.groups'),
]);
}
public function store()
{
$rules = [
'username' => 'required|min_length[3]|max_length[30]',
'email' => 'required|valid_email|max_length[254]',
'password' => 'required|min_length[8]',
'role' => 'required',
];
if (! $this->validate($rules)) {
if ($this->request->isAJAX()) {
return view('Setting/Admin/UserManagement/create', [
'roles' => setting('AuthGroups.groups'),
'errors' => $this->validator->getErrors(),
'formData' => $this->request->getPost(),
]);
}
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
$role = strtolower((string) $this->request->getPost('role'));
$allowedRoles = array_keys(setting('AuthGroups.groups'));
if (! in_array($role, $allowedRoles, true)) {
$errors = ['role' => 'Invalid role selected.'];
if ($this->request->isAJAX()) {
return view('Setting/Admin/UserManagement/create', [
'roles' => setting('AuthGroups.groups'),
'errors' => $errors,
'formData' => $this->request->getPost(),
]);
}
return redirect()->back()->withInput()->with('errors', $errors);
}
$user = new User();
$user->username = (string) $this->request->getPost('username');
$user->email = (string) $this->request->getPost('email');
$user->password = (string) $this->request->getPost('password');
$user->active = 1;
$userModel = model(UserModel::class);
$userModel->save($user);
$savedUser = $userModel->find($userModel->getInsertID());
if ($savedUser !== null) {
$savedUser->addGroup($role);
}
if ($this->request->isAJAX()) {
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
'message' => 'User created successfully.',
]);
}
return redirect()
->to('setting/admin/user-management')
->with('message', 'User created successfully.');
}
public function permissions(): string
{
$userModel = model(UserModel::class);
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
$selectedUserId = (int) $this->request->getGet('user_id');
$selectedUser = null;
if ($selectedUserId) {
$selectedUser = $userModel->withPermissions()->find($selectedUserId);
}
return view('Setting/Admin/UserManagement/permissions', [
'users' => $users,
'permissions' => setting('AuthGroups.permissions'),
'selectedUser' => $selectedUser,
]);
}
public function edit(int $userId): string
{
$userModel = model(UserModel::class);
$user = $userModel
->withIdentities()
->withGroups()
->find($userId);
if ($user === null) {
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
'errors' => ['User not found.'],
]);
}
$emailIdentity = $user->getEmailIdentity();
$email = $emailIdentity ? (string) ($emailIdentity->secret ?? '') : '';
$meta = $this->decodeIdentityExtra($emailIdentity->extra ?? null);
return view('Setting/Admin/UserManagement/edit', [
'user' => $user,
'email' => $email,
'person_name' => trim((string) ($meta['full_name'] ?? '')),
'phone_no' => trim((string) ($meta['phone_no'] ?? '')),
]);
}
public function update(int $userId)
{
$userModel = model(UserModel::class);
$user = $userModel->find($userId);
if ($user === null) {
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
'errors' => ['User not found.'],
]);
}
$username = trim((string) $this->request->getPost('username'));
$email = trim((string) $this->request->getPost('email'));
$personName = trim((string) $this->request->getPost('person_name'));
$phoneNo = trim((string) $this->request->getPost('phone_no'));
$password = (string) $this->request->getPost('password');
$active = $this->request->getPost('active') ? 1 : 0;
$errors = [];
if ($username === '' || strlen($username) < 3 || strlen($username) > 30 || preg_match('/\A[a-zA-Z0-9\.]+\z/', $username) !== 1) {
$errors['username'] = 'Login ID must be 3-30 chars and only letters, numbers, dot.';
}
if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Valid email is required.';
}
if ($personName !== '' && mb_strlen($personName) > 120) {
$errors['person_name'] = 'Person name must be maximum 120 characters.';
}
if ($phoneNo !== '' && preg_match('/^[0-9+\-()\s]{7,20}$/', $phoneNo) !== 1) {
$errors['phone_no'] = 'Phone format is invalid.';
}
if ($password !== '' && strlen($password) < 8) {
$errors['password'] = 'Password must be at least 8 characters.';
}
$tables = config('Auth')->tables;
$usersTable = (string) ($tables['users'] ?? 'users');
$identitiesTable = (string) ($tables['identities'] ?? 'auth_identities');
if (empty($errors)) {
$duplicateUsername = $this->db->table($usersTable)
->select('id')
->where('username', $username)
->where('id !=', $userId)
->get(1)
->getRowArray();
if (! empty($duplicateUsername)) {
$errors['username'] = 'Login ID already used by another user.';
}
$duplicateEmail = $this->db->table($identitiesTable)
->select('id')
->where('type', 'email_password')
->where('secret', $email)
->where('user_id !=', $userId)
->get(1)
->getRowArray();
if (! empty($duplicateEmail)) {
$errors['email'] = 'Email already used by another user.';
}
}
if (! empty($errors)) {
return view('Setting/Admin/UserManagement/edit', [
'user' => $userModel->withIdentities()->find($userId),
'email' => $email,
'person_name' => $personName,
'phone_no' => $phoneNo,
'errors' => $errors,
'formData' => [
'username' => $username,
'email' => $email,
'person_name' => $personName,
'phone_no' => $phoneNo,
'active' => $active,
],
]);
}
$user->username = $username;
$user->email = $email;
$user->active = $active;
if ($password !== '') {
$user->password = $password;
}
$userModel->save($user);
$this->syncIdentityMeta($userId, $personName, $phoneNo);
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
'message' => 'User updated successfully.',
]);
}
public function resetPasswordForm(int $userId): string
{
$userModel = model(UserModel::class);
$user = $userModel->find($userId);
if ($user === null) {
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
'errors' => ['User not found.'],
]);
}
return view('Setting/Admin/UserManagement/reset_password', [
'user' => $user,
]);
}
public function resetPassword(int $userId)
{
$userModel = model(UserModel::class);
$user = $userModel->find($userId);
if ($user === null) {
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
'errors' => ['User not found.'],
]);
}
$password = trim((string) $this->request->getPost('password'));
$passwordConfirm = trim((string) $this->request->getPost('password_confirm'));
$errors = [];
if (preg_match('/^\d{6}$/', $password) !== 1) {
$errors['password'] = 'Temporary PIN must be exactly 6 digits.';
}
if (! hash_equals($password, $passwordConfirm)) {
$errors['password_confirm'] = 'PIN and confirm PIN do not match.';
}
if (! empty($errors)) {
return view('Setting/Admin/UserManagement/reset_password', [
'user' => $user,
'errors' => $errors,
]);
}
$tables = config('Auth')->tables;
$identitiesTable = (string) ($tables['identities'] ?? 'auth_identities');
if (! $this->db->tableExists($identitiesTable)) {
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
'errors' => ['Auth identity table not found.'],
]);
}
$identity = $this->db->table($identitiesTable)
->select('id')
->where('user_id', $userId)
->where('type', 'email_password')
->get(1)
->getRowArray();
if (empty($identity['id'])) {
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
'errors' => ['User login identity not found.'],
]);
}
$this->db->table($identitiesTable)
->where('id', (int) $identity['id'])
->update([
'secret2' => password_hash($password, PASSWORD_DEFAULT),
'force_reset' => 1,
'updated_at' => date('Y-m-d H:i:s'),
]);
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/index', [
'users' => $users,
'message' => 'Temporary 6-digit PIN set successfully. User must change password on next login.',
]);
}
/**
* @return array<string, mixed>
*/
private function decodeIdentityExtra($extra): array
{
if (is_array($extra)) {
return $extra;
}
if (is_string($extra) && trim($extra) !== '') {
$decoded = json_decode($extra, true);
if (is_array($decoded)) {
return $decoded;
}
}
return [];
}
private function syncIdentityMeta(int $userId, string $personName, string $phoneNo): void
{
$tables = config('Auth')->tables;
$identitiesTable = (string) ($tables['identities'] ?? 'auth_identities');
if (! $this->db->tableExists($identitiesTable)) {
return;
}
$row = $this->db->table($identitiesTable)
->select('id,extra')
->where('user_id', $userId)
->where('type', 'email_password')
->get(1)
->getRowArray();
if (empty($row) || empty($row['id'])) {
return;
}
$payload = $this->decodeIdentityExtra($row['extra'] ?? null);
if ($personName === '') {
unset($payload['full_name']);
} else {
$payload['full_name'] = $personName;
}
if ($phoneNo === '') {
unset($payload['phone_no']);
} else {
$payload['phone_no'] = $phoneNo;
}
$this->db->table($identitiesTable)
->where('id', (int) $row['id'])
->update([
'extra' => empty($payload) ? null : json_encode($payload, JSON_UNESCAPED_UNICODE),
]);
}
public function savePermissions()
{
$rules = [
'user_id' => 'required|is_natural_no_zero',
];
if (! $this->validate($rules)) {
if ($this->request->isAJAX()) {
$users = model(UserModel::class)
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/permissions', [
'users' => $users,
'permissions' => setting('AuthGroups.permissions'),
'selectedUser' => null,
'errors' => $this->validator->getErrors(),
]);
}
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
$userId = (int) $this->request->getPost('user_id');
$permissions = (array) $this->request->getPost('permissions');
$permissions = array_values(array_filter(array_map('strtolower', $permissions)));
$allowedPermissions = array_keys(setting('AuthGroups.permissions'));
$permissions = array_values(array_intersect($permissions, $allowedPermissions));
$userModel = model(UserModel::class);
$user = $userModel->find($userId);
if ($user === null) {
$errors = ['user_id' => 'User not found.'];
if ($this->request->isAJAX()) {
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
return view('Setting/Admin/UserManagement/permissions', [
'users' => $users,
'permissions' => setting('AuthGroups.permissions'),
'selectedUser' => null,
'errors' => $errors,
]);
}
return redirect()->back()->withInput()->with('errors', $errors);
}
$user->syncPermissions(...$permissions);
if ($this->request->isAJAX()) {
$users = $userModel
->withIdentities()
->withGroups()
->findAll();
$selectedUser = $userModel->withPermissions()->find($userId);
return view('Setting/Admin/UserManagement/permissions', [
'users' => $users,
'permissions' => setting('AuthGroups.permissions'),
'selectedUser' => $selectedUser,
'message' => 'Permissions updated successfully.',
]);
}
return redirect()
->to('setting/admin/user-management/permissions?user_id=' . $userId)
->with('message', 'Permissions updated successfully.');
}
}