merge prod to main
This commit is contained in:
Regular → Executable
+136
-22
@@ -10,6 +10,8 @@ use CodeIgniter\I18n\Time;
|
||||
|
||||
class AuthorizedUsersController extends ResourceController
|
||||
{
|
||||
private const TOKEN_TTL_HOURS = 24;
|
||||
|
||||
protected $userModel;
|
||||
protected $authorizedUserModel;
|
||||
|
||||
@@ -18,6 +20,30 @@ class AuthorizedUsersController extends ResourceController
|
||||
$this->userModel = new UserModel();
|
||||
$this->authorizedUserModel = new AuthorizedUserModel();
|
||||
}
|
||||
|
||||
private function requireLogin()
|
||||
{
|
||||
if (!session()->get('is_logged_in')) {
|
||||
return $this->failUnauthorized('Authentication required.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function requireOwnership(array $authorizedUser)
|
||||
{
|
||||
$userId = (int) session()->get('user_id');
|
||||
if ($userId <= 0 || (int) ($authorizedUser['user_id'] ?? 0) !== $userId) {
|
||||
return $this->failForbidden('You do not have access to this resource.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function hashToken(string $token): string
|
||||
{
|
||||
return hash('sha256', $token);
|
||||
}
|
||||
/**
|
||||
* Return a list of authorized users for the logged-in main user.
|
||||
*
|
||||
@@ -25,7 +51,10 @@ class AuthorizedUsersController extends ResourceController
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
if ($resp = $this->requireLogin()) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
$userId = session()->get('user_id');
|
||||
$authorizedUsers = $this->authorizedUserModel->where('user_id', $userId)->findAll();
|
||||
|
||||
@@ -40,12 +69,20 @@ class AuthorizedUsersController extends ResourceController
|
||||
*/
|
||||
public function show($id = null)
|
||||
{
|
||||
if ($resp = $this->requireLogin()) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
$authorizedUser = $this->authorizedUserModel->find($id);
|
||||
|
||||
if (!$authorizedUser) {
|
||||
return $this->failNotFound('Authorized user not found.');
|
||||
}
|
||||
|
||||
if ($resp = $this->requireOwnership($authorizedUser)) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
return $this->respond($authorizedUser);
|
||||
}
|
||||
|
||||
@@ -56,6 +93,10 @@ class AuthorizedUsersController extends ResourceController
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
if ($resp = $this->requireLogin()) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
$email = strtolower($this->request->getPost('email'));
|
||||
|
||||
// Validate email
|
||||
@@ -66,19 +107,20 @@ class AuthorizedUsersController extends ResourceController
|
||||
$user = $this->userModel->where('email', $email)->first();
|
||||
|
||||
if (!$user) {
|
||||
return $this->failNotFound('No user found with this email.');
|
||||
return $this->respondCreated(['message' => 'Authorized user added. A confirmation email has been sent.']);
|
||||
}
|
||||
|
||||
// Generate a token for confirmation
|
||||
helper('text');
|
||||
$token = bin2hex(random_bytes(48));
|
||||
$tokenHash = $this->hashToken($token);
|
||||
|
||||
// Add entry to the authorized_users table
|
||||
$this->authorizedUserModel->insert([
|
||||
'user_id' => session()->get('user_id'), // Main user ID
|
||||
'authorized_user_id' => $user['id'],
|
||||
'email' => $email,
|
||||
'token' => $token,
|
||||
'token' => $tokenHash,
|
||||
'status' => 'Pending'
|
||||
]);
|
||||
|
||||
@@ -96,6 +138,10 @@ class AuthorizedUsersController extends ResourceController
|
||||
*/
|
||||
public function update($id = null)
|
||||
{
|
||||
if ($resp = $this->requireLogin()) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
// Fetch the authorized user
|
||||
$authorizedUser = $this->authorizedUserModel->find($id);
|
||||
|
||||
@@ -103,6 +149,10 @@ class AuthorizedUsersController extends ResourceController
|
||||
return $this->failNotFound('Authorized user not found.');
|
||||
}
|
||||
|
||||
if ($resp = $this->requireOwnership($authorizedUser)) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
// Update the authorized user’s information (e.g., email)
|
||||
$email = strtolower($this->request->getPost('email'));
|
||||
if ($email && filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
@@ -122,12 +172,20 @@ class AuthorizedUsersController extends ResourceController
|
||||
*/
|
||||
public function delete($id = null)
|
||||
{
|
||||
if ($resp = $this->requireLogin()) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
$authorizedUser = $this->authorizedUserModel->find($id);
|
||||
|
||||
if (!$authorizedUser) {
|
||||
return $this->failNotFound('Authorized user not found.');
|
||||
}
|
||||
|
||||
if ($resp = $this->requireOwnership($authorizedUser)) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
// Delete the authorized user record
|
||||
$this->authorizedUserModel->delete($id);
|
||||
|
||||
@@ -147,16 +205,28 @@ class AuthorizedUsersController extends ResourceController
|
||||
return $this->fail('Invalid confirmation link.');
|
||||
}
|
||||
|
||||
$authorizedUser = $this->authorizedUserModel->where('token', $token)->first();
|
||||
$tokenHash = $this->hashToken($token);
|
||||
$authorizedUser = $this->authorizedUserModel
|
||||
->groupStart()
|
||||
->where('token', $tokenHash)
|
||||
->orWhere('token', $token)
|
||||
->groupEnd()
|
||||
->where('created_at >=', Time::now()->subHours(self::TOKEN_TTL_HOURS)->toDateTimeString())
|
||||
->first();
|
||||
|
||||
if (!$authorizedUser) {
|
||||
return $this->fail('Invalid or expired confirmation link.');
|
||||
}
|
||||
|
||||
// Mark the authorized user as active
|
||||
$this->authorizedUserModel->update($authorizedUser['id'], ['status' => 'Active', 'token' => null]);
|
||||
// Mark the authorized user as active and rotate token for password setup
|
||||
$nextToken = bin2hex(random_bytes(48));
|
||||
$nextTokenHash = $this->hashToken($nextToken);
|
||||
$this->authorizedUserModel->update($authorizedUser['id'], [
|
||||
'status' => 'Active',
|
||||
'token' => $nextTokenHash,
|
||||
]);
|
||||
|
||||
return redirect()->to('/set_authorized_user_password/' . $authorizedUser['authorized_user_id']);
|
||||
return redirect()->to('/set_authorized_user_password/' . $authorizedUser['authorized_user_id'] . '?token=' . $nextToken);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,13 +237,36 @@ class AuthorizedUsersController extends ResourceController
|
||||
*/
|
||||
public function setPassword($authorizedUserId)
|
||||
{
|
||||
$token = (string) $this->request->getGet('token');
|
||||
if ($token === '') {
|
||||
return $this->fail('Invalid confirmation link.');
|
||||
}
|
||||
|
||||
$tokenHash = $this->hashToken($token);
|
||||
$authorizedUser = $this->authorizedUserModel
|
||||
->groupStart()
|
||||
->where('token', $tokenHash)
|
||||
->orWhere('token', $token)
|
||||
->groupEnd()
|
||||
->where('authorized_user_id', $authorizedUserId)
|
||||
->where('status', 'Active')
|
||||
->where('updated_at >=', Time::now()->subHours(self::TOKEN_TTL_HOURS)->toDateTimeString())
|
||||
->first();
|
||||
|
||||
if (!$authorizedUser) {
|
||||
return $this->fail('Invalid or expired confirmation link.');
|
||||
}
|
||||
|
||||
$user = $this->userModel->find($authorizedUserId);
|
||||
|
||||
if (!$user) {
|
||||
return $this->failNotFound('User not found.');
|
||||
}
|
||||
|
||||
return view('user/set_authorized_user_password', ['userId' => $authorizedUserId]);
|
||||
return view('user/set_authorized_user_password', [
|
||||
'userId' => $authorizedUserId,
|
||||
'token' => $token,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,38 +274,59 @@ class AuthorizedUsersController extends ResourceController
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
/*
|
||||
public function savePassword()
|
||||
public function savePassword($authorizedUserId = null)
|
||||
{
|
||||
// Validate the request
|
||||
$validation = \Config\Services::validation();
|
||||
$validation->setRules([
|
||||
'password' => 'required|min_length[6]',
|
||||
'password' => [
|
||||
'label' => 'Password',
|
||||
'rules' => 'required|min_length[8]|regex_match[/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@\\-=\\+*#$%&!?])[A-Za-z\\d@\\-=\\+*#$%&!?]{8,}$/]',
|
||||
],
|
||||
'password_confirm' => 'required|matches[password]',
|
||||
'user_id' => 'required|integer'
|
||||
'user_id' => 'required|integer',
|
||||
'token' => 'required',
|
||||
]);
|
||||
|
||||
if (!$this->validate($validation->getRules())) {
|
||||
return $this->failValidationErrors($validation->getErrors());
|
||||
}
|
||||
|
||||
// Get the validated input
|
||||
$userId = $this->request->getPost('user_id');
|
||||
$password = $this->request->getPost('password');
|
||||
$userId = (int) $this->request->getPost('user_id');
|
||||
$token = (string) $this->request->getPost('token');
|
||||
$authorizedUserId = $authorizedUserId !== null ? (int) $authorizedUserId : $userId;
|
||||
|
||||
$model = new UserModel();
|
||||
$user = $model->find($userId);
|
||||
if ($userId <= 0 || $authorizedUserId <= 0 || $userId !== $authorizedUserId) {
|
||||
return $this->fail('Invalid request.');
|
||||
}
|
||||
|
||||
$tokenHash = $this->hashToken($token);
|
||||
$authorizedUser = $this->authorizedUserModel
|
||||
->groupStart()
|
||||
->where('token', $tokenHash)
|
||||
->orWhere('token', $token)
|
||||
->groupEnd()
|
||||
->where('authorized_user_id', $authorizedUserId)
|
||||
->where('status', 'Active')
|
||||
->where('updated_at >=', Time::now()->subHours(self::TOKEN_TTL_HOURS)->toDateTimeString())
|
||||
->first();
|
||||
|
||||
if (!$authorizedUser) {
|
||||
return $this->fail('Invalid or expired confirmation link.');
|
||||
}
|
||||
|
||||
$user = $this->userModel->find($authorizedUserId);
|
||||
if (!$user) {
|
||||
return $this->failNotFound('User not found.');
|
||||
}
|
||||
|
||||
// Save the password
|
||||
$model->update($userId, ['password' => password_hash($password, PASSWORD_DEFAULT)]);
|
||||
$password = (string) $this->request->getPost('password');
|
||||
$hashedPassword = pbkdf2_hash($password);
|
||||
|
||||
$this->userModel->update($authorizedUserId, ['password' => $hashedPassword]);
|
||||
$this->authorizedUserModel->update($authorizedUser['id'], ['token' => null]);
|
||||
|
||||
return $this->respond(['message' => 'Password has been successfully set.']);
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Sends a confirmation email to the authorized user.
|
||||
*
|
||||
@@ -242,4 +356,4 @@ class AuthorizedUsersController extends ResourceController
|
||||
log_message('error', 'Failed to send authorized user confirmation email to ' . $email);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user