Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

90 lines
4.2 KiB
Markdown
Raw Permalink Normal View History

# Plugin Protection
MokoWaaS uses multiple layers of protection to prevent accidental or unauthorized disabling and uninstallation. The master user retains full control over the plugin at all times.
## Protection Layers
### 1. Protected Flag (`protected=1`)
During installation and on every admin session, MokoWaaS sets the `protected` column to `1` in the `#__extensions` database table for both `mokowaas` and `pkg_mokowaas` entries.
The Joomla framework itself enforces this flag: protected extensions cannot be disabled or uninstalled through the standard admin interface.
The `locked` column is set to `0` so the extension can still receive updates and configuration changes.
### 2. Self-Healing
The `ensureProtectedFlag()` method runs once per admin session (using a static flag to avoid repeated queries). If the `protected` column has been reset to `0` (e.g., by a database modification), it is automatically restored to `1`.
This runs in the `protectPlugin()` method, which is called from `onBeforeRender()` on every admin page load.
### 3. Hidden from Plugin List
For non-master users, MokoWaaS injects JavaScript on the `com_plugins` and `com_installer` pages that hides any table row containing "mokowaas" or "MokoWaaS". This prevents non-master users from seeing the plugin in the extension list.
The `hidePluginFromList()` method adds an inline script that runs on `DOMContentLoaded`:
```javascript
document.querySelectorAll('tr').forEach(function(row) {
var text = row.textContent || '';
if (text.indexOf('mokowaas') !== -1 || text.indexOf('MokoWaaS') !== -1) {
row.style.display = 'none';
}
});
```
### 4. onExtensionBeforeSave Interception
The `onExtensionBeforeSave` event handler intercepts save attempts on the plugin configuration. If a non-master user attempts to save the plugin with `enabled = 0`, the handler:
1. Displays an error message: "MokoWaaS cannot be disabled."
2. Forces `enabled` back to `1` on the table object
3. Returns `true` to allow the save to proceed (with the corrected value)
### 5. protectPlugin() -- Uninstall and Disable Blocking
The `protectPlugin()` method runs on every admin page request and checks for active uninstall or disable attempts:
**Uninstall blocking**: If the current request is to `com_installer` with task `manage.remove`, and the extension IDs include any MokoWaaS extension, the request is blocked with an error message and a redirect back to the installer manage view.
**Disable blocking**: If the current request is to `com_plugins` with task `plugins.publish`, and the extension IDs include MokoWaaS, the request is blocked with an error message and a redirect back to the plugins list.
The `isOurExtension()` helper method checks extension IDs against the database to determine if they belong to MokoWaaS (matching on element name `mokowaas` or `pkg_mokowaas`).
## Master User Exemption
All protection checks call `isMasterUser()` first. If the current user is the designated master user (matching the configured `master_username`), all protections are bypassed. The master user can:
- See MokoWaaS in the plugin and extension lists
- Disable the plugin via the configuration page
- Uninstall the plugin via the Extension Manager
- Modify all plugin settings
## Installation-Time Protection
The package installer script (`Pkg_MokowaasInstallerScript`) and the plugin installer script both set `protected=1` during `postflight`:
- `enableAndLockPlugin()` sets `enabled=1`, `locked=1`, `protected=1` on the system plugin
- `protectExtensions()` sets `protected=1`, `locked=0` on all MokoWaaS extensions (plugin and package)
This ensures protection is active immediately after installation, before the first admin page load triggers the self-healing logic.
## Summary of Protection Flow
```
Installation
-> postflight sets protected=1, enabled=1
Every admin page load (onBeforeRender)
-> protectPlugin()
-> ensureProtectedFlag() (once per session, restores protected=1 if needed)
-> if not master user:
-> block uninstall attempts (com_installer manage.remove)
-> block disable attempts (com_plugins plugins.publish)
-> hidePluginFromList() for non-master users
Plugin config save (onExtensionBeforeSave)
-> if not master user and enabled=0:
-> force enabled=1, show error
```