Adding & Removing Users on Fork CMS | OpsBlu Docs

Adding & Removing Users on Fork CMS

Adding & Removing Users on Fork CMS — setup, configuration, and best practices for Fork CMS.

Fork CMS is a PHP-based open-source CMS that stores users in a MySQL database. Backend users (editors and admins) are managed through the Fork CMS backend at /private/en/ (locale-prefixed). Fork CMS uses a group-based permission system where each user belongs to one or more groups with module-level access controls.

Adding Users via the Backend

Creating a New User

  1. Log in to the Fork CMS backend (e.g., https://your-site.com/private/en/)
  2. Navigate to Settings > Users (or Users in the main navigation, depending on your version)
  3. Click Add user
  4. Fill in the Account tab:
    • Email (used for login and notifications)
    • Password (and confirmation)
    • Display Name (shown in the backend and on content)
    • Active: checked
  5. Under the Groups tab, select one or more groups:
    • Admin -- Full backend access to all modules and settings
    • Pages -- Access to the Pages module only
    • Custom groups as configured
  6. Fill in optional Profile fields (avatar, biography, etc.)
  7. Click Add

Managing Groups and Permissions

Fork CMS defines permissions at the group level. Each group specifies which backend modules and actions are accessible:

  1. Go to Settings > Groups
  2. Click Add group or edit an existing group
  3. Configure module-level permissions:
Group Permission Structure:
├── Dashboard (view)
├── Pages (add, edit, delete)
├── Blog (add, edit, edit_own, delete, categories)
├── Search (manage, statistics)
├── Content Blocks (add, edit, delete)
├── FAQ (add, edit, delete, categories)
├── Location (add, edit, delete)
├── Tags (manage)
├── Profiles (manage, groups)
└── Settings (general, modules, themes, translations)

Each module action has a checkbox. Users inherit permissions from all groups they belong to (union of permissions).

Adding Users via Direct Database

For scripted or automated user creation:

-- Create a new backend user in Fork CMS
-- Fork CMS uses MD5 hashed passwords (legacy) or bcrypt (newer versions)

-- Step 1: Insert the user record
INSERT INTO users (email, password, active, is_god, date_added)
VALUES (
  'jsmith@example.com',
  -- For bcrypt: php -r "echo password_hash('Pass123!', PASSWORD_BCRYPT);"
  '$2y$10$hashed_password_here',
  'Y',
  'N',
  NOW()
);

SET @uid = LAST_INSERT_ID();

-- Step 2: Add user settings (display name, etc.)
INSERT INTO users_settings (user_id, name, value)
VALUES
  (@uid, 'nickname', 's:10:"John Smith";'),
  (@uid, 'date_format', 's:5:"d/m/Y";'),
  (@uid, 'time_format', 's:5:"H:i:s";'),
  (@uid, 'number_format', 's:12:"dot_nothing";'),
  (@uid, 'avatar', 's:11:"no-avatar.gif";');

-- Step 3: Add user to a group
-- Get the admin group ID first
INSERT INTO users_groups (user_id, group_id)
SELECT @uid, id FROM groups WHERE name = 'Admin';

CLI User Creation Script

#!/bin/bash
# create-fork-user.sh -- Create a Fork CMS user from the command line

DB_NAME="fork_cms"
DB_USER="fork_user"
DB_PASS="db_password"

read -p "Email: " EMAIL
read -p "Display Name: " NICKNAME
read -s -p "Password: " PASSWORD
echo
read -p "Group (Admin/Pages): " GROUP

HASH=$(php -r "echo password_hash('$PASSWORD', PASSWORD_BCRYPT);")

mysql -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" <<SQL
INSERT INTO users (email, password, active, is_god, date_added)
VALUES ('$EMAIL', '$HASH', 'Y', 'N', NOW());
SET @uid = LAST_INSERT_ID();
INSERT INTO users_settings (user_id, name, value)
VALUES (@uid, 'nickname', 's:${#NICKNAME}:"$NICKNAME";');
INSERT INTO users_groups (user_id, group_id)
SELECT @uid, id FROM groups WHERE name = '$GROUP';
SQL

echo "User created: $EMAIL ($GROUP)"

Removing and Deactivating Users

Deactivating a User

  1. Go to Settings > Users
  2. Click Edit next to the user
  3. Uncheck the Active checkbox
  4. Click Save

Deactivated users cannot log in to the backend. Their content and attribution remain intact.

Deleting a User

  1. Go to Settings > Users
  2. Click the Delete icon next to the user
  3. Confirm the deletion

What Happens to Their Content

When you delete a Fork CMS user:

  • Blog posts authored by the user remain published but the author reference may show as empty
  • Pages are not tied to individual users in the same way -- page revisions store the user ID, but pages persist
  • Content blocks created by the user remain active
  • The users_settings and users_groups records are deleted along with the user record
  • Revision history retains references to the deleted user's ID, which resolves to "Unknown"
  • Media uploads remain in the filesystem under /src/Frontend/Files/

SQL-Based Deactivation

-- Deactivate a user
UPDATE users SET active = 'N' WHERE email = 'jsmith@example.com';

-- Delete a user and all associated records
SET @uid = (SELECT id FROM users WHERE email = 'jsmith@example.com');
DELETE FROM users_groups WHERE user_id = @uid;
DELETE FROM users_settings WHERE user_id = @uid;
DELETE FROM users WHERE id = @uid;

Bulk User Management

Export All Users

-- Export all backend users with their groups
SELECT u.id, u.email, u.active,
       (SELECT value FROM users_settings WHERE user_id = u.id AND name = 'nickname') AS nickname,
       GROUP_CONCAT(g.name) AS groups
FROM users u
LEFT JOIN users_groups ug ON u.id = ug.user_id
LEFT JOIN groups g ON ug.group_id = g.id
GROUP BY u.id
ORDER BY u.email;

Bulk Import Script

<?php
// bulk-import-fork-users.php
// Usage: php bulk-import-fork-users.php < users.csv

$pdo = new PDO(
    'mysql:host=localhost;dbname=fork_cms',
    'fork_user',
    getenv('DB_PASSWORD')
);

$csv = array_map('str_getcsv', file('php://stdin'));
$header = array_shift($csv);

foreach ($csv as $row) {
    $data = array_combine($header, $row);

    // Check for existing user
    $stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?");
    $stmt->execute([$data['email']]);
    if ($stmt->fetch()) {
        echo "SKIP: {$data['email']} (exists)\n";
        continue;
    }

    $hash = password_hash($data['password'], PASSWORD_BCRYPT);

    $pdo->prepare("INSERT INTO users (email, password, active, is_god, date_added)
                    VALUES (?, ?, 'Y', 'N', NOW())")
         ->execute([$data['email'], $hash]);
    $uid = $pdo->lastInsertId();

    $nickname = $data['name'];
    $serialized = 's:' . strlen($nickname) . ':"' . $nickname . '";';
    $pdo->prepare("INSERT INTO users_settings (user_id, name, value) VALUES (?, 'nickname', ?)")
         ->execute([$uid, $serialized]);

    $pdo->prepare("INSERT INTO users_groups (user_id, group_id)
                    SELECT ?, id FROM groups WHERE name = ?")
         ->execute([$uid, $data['group']]);

    echo "ADDED: {$data['email']} ({$data['group']})\n";
}
echo "Import complete.\n";

Authentication and Security

Fork CMS does not natively support LDAP or SAML SSO. Authentication is local, using the users table.

Security Configuration

Fork CMS security settings are in app/config/parameters.yml and the database:

# app/config/parameters.yml
parameters:
  site.default_language: en
  fork.debug_mode: false
  fork.debug_email: admin@example.com

Brute-Force Protection

Fork CMS does not include built-in login rate limiting. Protect the backend with server-level configuration:

# .htaccess -- Rate limit the backend login
<Location /private>
  <Limit POST>
    # Use mod_evasive or mod_security for rate limiting
    SetEnvIf Request_URI "/authentication" rate_limit
  </Limit>
</Location>
# nginx -- Rate limit backend login
limit_req_zone $binary_remote_addr zone=fork_login:10m rate=5r/m;

location /private {
  location ~ /authentication {
    limit_req zone=fork_login burst=3;
    # ... pass to PHP-FPM
  }
}

Two-Factor Authentication

Fork CMS does not include built-in 2FA. To add it, implement a custom module or use a reverse proxy with 2FA (e.g., Cloudflare Access, Authelia):

# Example: Authelia protection for Fork CMS backend
# docker-compose.yml addition
services:
  authelia:
    image: authelia/authelia
    volumes:
      - ./authelia/config:/config
    environment:
      - TZ=America/Chicago

Offboarding Checklist

  1. Deactivate the user -- Set active = 'N' to preserve content attribution
  2. Remove from all groups -- Clear group memberships before deactivation
  3. Review blog posts -- Reassign authored blog posts if author attribution matters
  4. Check content blocks -- Verify no content blocks reference the departing user
  5. Clear sessions -- Delete the user's session data from the sessions table
  6. Audit recent revisions -- Review page revision history for recent changes by the user
  7. Change shared credentials -- If the user knew the database password or had SSH access, rotate those credentials
  8. Update server access -- Remove their SSH keys and any IP allowlist entries