justinholtweb/craft-icecube

Lock entries, assets, categories, and globals behind a password in Craft CMS.

Maintainers

Package info

github.com/justinholtweb/craft-icecube

Type:craft-plugin

pkg:composer/justinholtweb/craft-icecube

Transparency log

Statistics

Installs: 14

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

5.2.0 2026-07-22 11:22 UTC

This package is auto-updated.

Last update: 2026-07-22 11:23:08 UTC


README

Lock entries, assets, categories, and globals behind a password in Craft CMS.

Requirements

  • Craft CMS 5.3.0+
  • PHP 8.2+

Installation

composer require justinholtweb/craft-icecube
php craft plugin/install icecube

Features

  • Lock individual entries, assets, categories, and global sets
  • Protect against editing, deleting, or both
  • Per-lock passwords or a single master password
  • Configurable unlock session duration (1-1440 minutes)
  • Optional notes on each lock explaining why it's locked
  • Admin bypass setting
  • Granular user permissions

Configuration

Visit Settings > Icecube in the control panel to configure:

  • Master Password -- fallback password when no per-lock password is set
  • Admins Bypass -- allow admin users to skip all locks automatically
  • Element Types -- enable/disable locking for entries, assets, categories, and globals individually
  • Unlock TTL -- how long an unlock session stays valid (default: 10 minutes)
  • Failed Attempts Allowed -- wrong passwords before a lockout (default: 5, max 100)
  • Lockout Duration -- how long that user waits before retrying (default: 5 minutes)

Permissions

Icecube registers three permissions under Settings > Users > Permissions:

Permission Description
Manage locks Create, edit, and delete locks
Bypass all locks Skip locks entirely without entering a password
Unlock locked content Enter a password to unlock content

Non-admin users need Unlock locked content before they can enter a password; without it they simply can't save or delete locked content. Admins satisfy every permission check automatically, so this only affects non-admins.

Unlock attempts are rate limited: after a number of failed attempts against the same element and action, that user is refused for a cooling-off period. Both the threshold and the duration are configurable in Settings → Icecube (defaults: 5 attempts, 5 minutes). Counting is per user, so one editor's mistyping can't lock out the team, and a successful unlock clears the count.

How It Works

  1. Create a lock targeting a specific entry, asset, category, or global set
  2. Choose whether to lock editing, deleting, or both
  3. Optionally set a per-lock password and add notes
  4. When a user tries to save or delete a locked element, they'll see an unlock modal prompting for the password
  5. A successful unlock grants a time-limited session (configurable via Unlock TTL)

Development

Local tooling runs in DDEV (config is gitignored, not part of the distributed plugin):

ddev start
ddev composer install
ddev composer phpstan            # static analysis (level 5)
ddev composer test               # fast unit tests
ddev composer test-integration   # Craft-backed integration tests
ddev composer test-all           # both suites

Test suites

Suite Location Harness Covers
Unit tests/unit Plain PHPUnit, Yii only Settings validation, password hashing, translation integrity
Integration tests/integration Codeception + Craft test module Lock resolution, session unlocks, save/delete enforcement, install migration

The integration suite boots a real Craft application against a throwaway test database (credentials in tests/.env, defaulting to DDEV). It wipes and reinstalls that database on every run, and each test runs inside a transaction that is rolled back afterwards.

Two constraints are worth knowing before adding tests:

  • Don't write project config. Anything that applies project config — including saveSection(), saveEntryType(), and Plugins::savePluginSettings() — deadlocks in this harness and leaves the database holding metadata locks. Build elements in memory instead (new Entry(['id' => 100, 'sectionId' => 10])); the plugin's services only read an element's class and a couple of ids. If a run does hang, recover with DROP DATABASE test; CREATE DATABASE test;.
  • The charset is pinned to utf8mb4 in tests/_craft/config/db.php, because Craft's default collation is incompatible with its default utf8 charset on MariaDB.

License

Craft License -- see LICENSE.md.