Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions backend/app/Controllers/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function register(array $params): void
}

// Create user
$passwordHash = password_hash($data['password'], PASSWORD_BCRYPT, ['cost' => 12]);
$passwordHash = password_hash($data['password'], PASSWORD_ARGON2ID);
$role = $data['role'] ?? 'user';

// Only admin can create admin users
Expand Down Expand Up @@ -242,8 +242,16 @@ public function changePassword(array $params): void
return;
}

if (strlen($data['new_password']) < 8) {
$this->json(['error' => 'New password must be at least 8 characters long.'], 422);
if (strlen($data['new_password']) < 10) {
$this->json(['error' => 'New password must be at least 10 characters long.'], 422);
return;
}

// Enforce same complexity as registration
if (!preg_match('/[A-Z]/', $data['new_password']) ||
!preg_match('/[a-z]/', $data['new_password']) ||
!preg_match('/[0-9]/', $data['new_password'])) {
$this->json(['error' => 'Password must contain uppercase, lowercase, and a number.'], 422);
return;
}

Expand All @@ -257,7 +265,7 @@ public function changePassword(array $params): void
return;
}

$newHash = password_hash($data['new_password'], PASSWORD_BCRYPT, ['cost' => 12]);
$newHash = password_hash($data['new_password'], PASSWORD_ARGON2ID);
$stmt = $db->prepare('UPDATE users SET password_hash = :hash, updated_at = NOW() WHERE id = :id');
$stmt->execute(['hash' => $newHash, 'id' => $authUser['id']]);

Expand Down
94 changes: 94 additions & 0 deletions backend/app/Controllers/EbookPluginController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace App\Controllers;

use App\Config\Database;

/**
* EbookPlugin Controller
* Manages the e-book plugin enable/disable state (admin only)
*/
class EbookPluginController extends BaseController
{
/**
* GET /api/ebook-plugin/status
* Returns whether the e-book plugin is enabled
*/
public function status(array $params): void
{
$db = Database::getConnection();

$stmt = $db->prepare("
SELECT setting_value FROM plugin_settings
WHERE plugin_name = 'ebooks' AND setting_key = 'enabled'
");
$stmt->execute();
$row = $stmt->fetch();

$enabled = $row && $row['setting_value'] === 'true';
$this->json(['enabled' => $enabled]);
}

/**
* POST /api/ebook-plugin/enable
* Enable the e-book plugin (admin only)
*/
public function enable(array $params): void
{
$this->setPluginState('true');
$authUser = $this->getAuthUser();
$this->logPluginAction('enable', $authUser['id'] ?? null);
$this->json(['message' => 'E-book plugin enabled.', 'enabled' => true]);
}

/**
* POST /api/ebook-plugin/disable
* Disable the e-book plugin (admin only)
*/
public function disable(array $params): void
{
$this->setPluginState('false');
$authUser = $this->getAuthUser();
$this->logPluginAction('disable', $authUser['id'] ?? null);
$this->json(['message' => 'E-book plugin disabled.', 'enabled' => false]);
}

// ---- Private helpers ----

private function setPluginState(string $value): void
{
$db = Database::getConnection();
$stmt = $db->prepare("
INSERT INTO plugin_settings (plugin_name, setting_key, setting_value, updated_at)
VALUES ('ebooks', 'enabled', :value, NOW())
ON CONFLICT (plugin_name, setting_key)
DO UPDATE SET setting_value = :value, updated_at = NOW()
");
$stmt->execute(['value' => $value]);
}

private function logPluginAction(string $action, ?string $userId): void
{
$db = Database::getConnection();

// Verify the user actually exists in this DB instance before inserting.
// After a full rebuild the JWT may contain a UUID from a previous database.
if ($userId !== null) {
$check = $db->prepare("SELECT 1 FROM users WHERE id = :id");
$check->execute(['id' => $userId]);
if (!$check->fetch()) {
$userId = null; // ghost user — log without user reference
}
}

$stmt = $db->prepare("
INSERT INTO audit_log (user_id, action, entity_type, entity_id, details)
VALUES (:user_id, :action, 'plugin', NULL, :details)
");
$stmt->execute([
'user_id' => $userId,
'action' => $action,
'details' => json_encode(['plugin' => 'ebooks']),
]);
}
}
Loading
Loading