00d44256b4
Rebrand all 17 sub-extensions from mokowaas to mokosuite naming, including component, plugins, modules, task plugins, and webservices. Updates package manifest, workflows, docs, wiki, and issue templates. Adds new plg_system_mokosuite_license extension.
237 lines
6.0 KiB
PHP
237 lines
6.0 KiB
PHP
<?php
|
|
/**
|
|
* @package MokoSuite
|
|
* @subpackage com_mokosuite
|
|
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
|
* @license GNU General Public License version 3 or later; see LICENSE
|
|
*/
|
|
|
|
namespace Moko\Component\MokoSuite\Api\Controller;
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
use Joomla\CMS\Factory;
|
|
use Joomla\CMS\MVC\Controller\BaseController;
|
|
|
|
/**
|
|
* Provision reset API controller.
|
|
*
|
|
* POST /api/index.php/v1/mokosuite/provision-reset
|
|
*
|
|
* Resets a site for new client provisioning: clears hits, versions,
|
|
* download keys, and flags the site for fresh client info collection.
|
|
* Used after copying a demo site to create a new client install.
|
|
*
|
|
* @since 02.35.00
|
|
*/
|
|
class ProvisionController extends BaseController
|
|
{
|
|
/**
|
|
* Reset the site for new client provisioning.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function execute($task = 'provision'): void
|
|
{
|
|
$app = Factory::getApplication();
|
|
$user = $app->getIdentity();
|
|
|
|
if (!$user->authorise('core.manage', 'com_mokosuite'))
|
|
{
|
|
$this->sendJson(403, ['error' => 'Not authorized']);
|
|
|
|
return;
|
|
}
|
|
|
|
if ($app->input->getMethod() !== 'POST')
|
|
{
|
|
$this->sendJson(405, ['error' => 'POST required']);
|
|
|
|
return;
|
|
}
|
|
|
|
$db = Factory::getDbo();
|
|
$results = [];
|
|
|
|
// 1. Reset article hit counters
|
|
try
|
|
{
|
|
$db->setQuery(
|
|
$db->getQuery(true)
|
|
->update($db->quoteName('#__content'))
|
|
->set($db->quoteName('hits') . ' = 0')
|
|
)->execute();
|
|
$results['hits_reset'] = $db->getAffectedRows();
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
$results['hits_reset'] = 'error: ' . $e->getMessage();
|
|
}
|
|
|
|
// 2. Delete content version history
|
|
try
|
|
{
|
|
$db->setQuery(
|
|
$db->getQuery(true)->delete($db->quoteName('#__history'))
|
|
)->execute();
|
|
$results['versions_deleted'] = $db->getAffectedRows();
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
$results['versions_deleted'] = 'error: ' . $e->getMessage();
|
|
}
|
|
|
|
// 3. Regenerate heartbeat token if requested
|
|
$input = $app->getInput()->json;
|
|
$resetToken = (bool) ($input->get('reset_token', false, 'BOOLEAN'));
|
|
|
|
if ($resetToken)
|
|
{
|
|
try
|
|
{
|
|
$newToken = bin2hex(random_bytes(32));
|
|
|
|
$plugin = \Joomla\CMS\Plugin\PluginHelper::getPlugin('system', 'mokosuite');
|
|
|
|
if ($plugin)
|
|
{
|
|
$pluginParams = new \Joomla\Registry\Registry($plugin->params);
|
|
$pluginParams->set('health_api_token', $newToken);
|
|
|
|
$db->setQuery(
|
|
$db->getQuery(true)
|
|
->update($db->quoteName('#__extensions'))
|
|
->set($db->quoteName('params') . ' = ' . $db->quote($pluginParams->toString()))
|
|
->where($db->quoteName('element') . ' = ' . $db->quote('mokosuite'))
|
|
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
|
|
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
|
|
)->execute();
|
|
|
|
$results['token_regenerated'] = true;
|
|
$results['new_token'] = $newToken;
|
|
}
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
$results['token_regenerated'] = 'error: ' . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
// 4. Reset all user API tokens if requested
|
|
$resetApiTokens = (bool) ($input->get('reset_api_tokens', false, 'BOOLEAN'));
|
|
|
|
if ($resetApiTokens)
|
|
{
|
|
try
|
|
{
|
|
// Get users who have API tokens before deleting
|
|
$db->setQuery(
|
|
$db->getQuery(true)
|
|
->select('DISTINCT ' . $db->quoteName('user_id'))
|
|
->from($db->quoteName('#__user_keys'))
|
|
->where($db->quoteName('series') . ' LIKE ' . $db->quote('api-%'))
|
|
);
|
|
$affectedUserIds = $db->loadColumn() ?: [];
|
|
|
|
$db->setQuery(
|
|
$db->getQuery(true)->delete($db->quoteName('#__user_keys'))
|
|
->where($db->quoteName('series') . ' LIKE ' . $db->quote('api-%'))
|
|
)->execute();
|
|
$results['api_tokens_revoked'] = $db->getAffectedRows();
|
|
|
|
// Notify affected users
|
|
if (!empty($affectedUserIds))
|
|
{
|
|
$this->notifyTokenReset($db, $affectedUserIds);
|
|
$results['users_notified'] = \count($affectedUserIds);
|
|
}
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
$results['api_tokens_revoked'] = 'error: ' . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
// 5. Flag site for fresh client info setup
|
|
try
|
|
{
|
|
// Write a flag file that the core plugin checks on next admin load
|
|
$flagFile = JPATH_ADMINISTRATOR . '/cache/mokosuite_setup_required.flag';
|
|
file_put_contents($flagFile, json_encode([
|
|
'created' => gmdate('Y-m-d\TH:i:s\Z'),
|
|
'reason' => 'provision-reset',
|
|
'remote_ip' => $_SERVER['REMOTE_ADDR'] ?? '',
|
|
]));
|
|
$results['setup_flag'] = true;
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
$results['setup_flag'] = 'error: ' . $e->getMessage();
|
|
}
|
|
|
|
$this->sendJson(200, [
|
|
'status' => 'ok',
|
|
'message' => 'Site provisioned for new client.',
|
|
'results' => $results,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Notify users that their API tokens have been revoked.
|
|
*/
|
|
private function notifyTokenReset($db, array $userIds): void
|
|
{
|
|
try
|
|
{
|
|
$db->setQuery(
|
|
$db->getQuery(true)
|
|
->select([$db->quoteName('name'), $db->quoteName('email')])
|
|
->from($db->quoteName('#__users'))
|
|
->whereIn($db->quoteName('id'), $userIds)
|
|
->where($db->quoteName('block') . ' = 0')
|
|
);
|
|
$users = $db->loadObjectList() ?: [];
|
|
|
|
$config = Factory::getConfig();
|
|
$siteName = $config->get('sitename', 'Joomla');
|
|
$siteUrl = rtrim(\Joomla\CMS\Uri\Uri::root(), '/');
|
|
|
|
$mailer = Factory::getMailer();
|
|
|
|
foreach ($users as $u)
|
|
{
|
|
try
|
|
{
|
|
$mailer->clearAllRecipients();
|
|
$mailer->addRecipient($u->email, $u->name);
|
|
$mailer->setSubject($siteName . ' — API tokens have been reset');
|
|
$mailer->setBody(
|
|
"Hello {$u->name},\n\n"
|
|
. "Your API access tokens on {$siteName} have been revoked by an administrator.\n\n"
|
|
. "If you use API integrations, please log in and generate a new token:\n"
|
|
. "{$siteUrl}/administrator/\n\n"
|
|
. "— {$siteName}"
|
|
);
|
|
$mailer->send();
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
// Non-critical
|
|
}
|
|
}
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
// Non-critical
|
|
}
|
|
}
|
|
|
|
private function sendJson(int $code, array $data): void
|
|
{
|
|
http_response_code($code);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode($data, JSON_UNESCAPED_SLASHES);
|
|
Factory::getApplication()->close();
|
|
}
|
|
}
|