Adding & Removing Users on eZ Platform | OpsBlu Docs

Adding & Removing Users on eZ Platform

Adding & Removing Users on eZ Platform — setup, configuration, and best practices for eZ Platform.

eZ Platform (now Ibexa DXP) stores users as content objects in its repository. Every user is a Content item of type "User" placed under the /Users/ content tree. User management is done through the admin UI, the PHP API, or the REST API. Roles and policies provide granular access control.

Adding Users via the Admin UI

Creating a New User

  1. Log in to the eZ Platform admin at https://your-site.com/admin
  2. Navigate to Admin > Users (or browse the Content tree to /Users/)
  3. Select a User Group to place the user in (e.g., Editors, Administrators)
  4. Click Create content and select User as the content type
  5. Fill in the required fields:
    • First Name
    • Last Name
    • User account section:
      • Login (username, must be unique)
      • Email (must be unique)
      • Password (and confirmation)
      • Enabled: checked
  6. Click Publish

The user can immediately log in at /admin or the frontend (depending on role policies).

User Groups

eZ Platform organizes users into User Groups (which are themselves content objects). Default groups:

Group Path Purpose
/Users/Administrators/ Full admin access
/Users/Editors/ Content editing access
/Users/Members/ Frontend registered users
/Users/Anonymous Users/ Unauthenticated visitors (system group)

You can create custom groups by adding new content of type "User Group" under /Users/.

Adding Users via the PHP API

The eZ Platform PHP API provides programmatic user creation:

<?php
// src/Command/CreateUserCommand.php
namespace App\Command;

use eZ\Publish\API\Repository\RepositoryInterface;
use eZ\Publish\API\Repository\Values\User\UserCreateStruct;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CreateUserCommand extends Command
{
    protected static $defaultName = 'app:create-user';
    private RepositoryInterface $repository;

    public function __construct(RepositoryInterface $repository)
    {
        parent::__construct();
        $this->repository = $repository;
    }

    protected function configure(): void
    {
        $this
            ->addArgument('login', InputArgument::REQUIRED)
            ->addArgument('email', InputArgument::REQUIRED)
            ->addArgument('password', InputArgument::REQUIRED)
            ->addArgument('first-name', InputArgument::REQUIRED)
            ->addArgument('last-name', InputArgument::REQUIRED)
            ->addArgument('group-id', InputArgument::OPTIONAL, 'User group content ID', '13');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->repository->getPermissionResolver()->setCurrentUserReference(
            $this->repository->getUserService()->loadUser(14) // Admin user
        );

        $userService = $this->repository->getUserService();
        $contentTypeService = $this->repository->getContentTypeService();

        $userContentType = $contentTypeService->loadContentTypeByIdentifier('user');
        $userGroup = $userService->loadUserGroup($input->getArgument('group-id'));

        $userCreateStruct = $userService->newUserCreateStruct(
            $input->getArgument('login'),
            $input->getArgument('email'),
            $input->getArgument('password'),
            'eng-GB',
            $userContentType
        );
        $userCreateStruct->setField('first_name', $input->getArgument('first-name'));
        $userCreateStruct->setField('last_name', $input->getArgument('last-name'));
        $userCreateStruct->enabled = true;

        $user = $userService->createUser($userCreateStruct, [$userGroup]);
        $output->writeln("Created user: {$user->login} (ID: {$user->id})");

        return Command::SUCCESS;
    }
}
# Create a user via the custom command
php bin/console app:create-user jsmith jsmith@example.com "SecurePass123!" John Smith

# Or create a user with the built-in command (Ibexa DXP 4+)
php bin/console ibexa:user:create jsmith jsmith@example.com "SecurePass123!" \
  --first-name=John --last-name=Smith --user-group=13

Adding Users via the REST API

eZ Platform exposes a REST API for user management:

# Create a new user via REST API
curl -X POST "https://your-site.com/api/ezp/v2/user/users" \
  -H "Content-Type: application/vnd.ez.api.UserCreate+json" \
  -H "Accept: application/json" \
  -u admin:publish \
  -d '{
    "UserCreate": {
      "mainLanguageCode": "eng-GB",
      "ContentType": {"_href": "/api/ezp/v2/content/types/4"},
      "login": "jsmith",
      "email": "jsmith@example.com",
      "password": "SecurePass123!",
      "enabled": true,
      "fields": {
        "field": [
          {"fieldDefinitionIdentifier": "first_name", "fieldValue": "John"},
          {"fieldDefinitionIdentifier": "last_name", "fieldValue": "Smith"}
        ]
      },
      "ParentGroup": {"_href": "/api/ezp/v2/user/groups/1/5/13"}
    }
  }'

# List users in a group
curl -s "https://your-site.com/api/ezp/v2/user/groups/1/5/13/users" \
  -H "Accept: application/json" \
  -u admin:publish

# Get a specific user
curl -s "https://your-site.com/api/ezp/v2/user/users/USERID" \
  -H "Accept: application/json" \
  -u admin:publish

Removing and Deactivating Users

  1. Go to Admin > Users
  2. Find and click the user
  3. Click Edit
  4. In the User account section, uncheck Enabled
  5. Click Publish

Disabled users cannot log in. Their content remains published and properly attributed.

Deleting a User

  1. Navigate to the user in the Content tree under /Users/
  2. Click Move to Trash (or Delete)
  3. Confirm the deletion

What Happens to Their Content

When you delete a user in eZ Platform:

  • Content created by the user is NOT deleted -- it remains in the repository
  • The owner and modifier fields on content objects retain the user ID, but name resolution returns empty
  • Content versions (drafts, archives) retain the original creator reference
  • Workflow tasks assigned to the user become orphaned
  • The user's Content object goes to trash (recoverable until trash is emptied)
  • Subtree permissions that referenced the user's group are unaffected (policies are group-based)

API-Based Deactivation

# Disable a user via REST API
curl -X PATCH "https://your-site.com/api/ezp/v2/user/users/USERID" \
  -H "Content-Type: application/vnd.ez.api.UserUpdate+json" \
  -H "Accept: application/json" \
  -u admin:publish \
  -d '{"UserUpdate": {"enabled": false}}'

# Delete a user via REST API
curl -X DELETE "https://your-site.com/api/ezp/v2/user/users/USERID" \
  -u admin:publish
// Disable a user via PHP API
$userService = $repository->getUserService();
$user = $userService->loadUserByLogin('jsmith');

$userUpdateStruct = $userService->newUserUpdateStruct();
$userUpdateStruct->enabled = false;

$userService->updateUser($user, $userUpdateStruct);

Bulk User Management

Bulk Import via PHP Command

<?php
// src/Command/BulkImportUsersCommand.php

namespace App\Command;

use eZ\Publish\API\Repository\RepositoryInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class BulkImportUsersCommand extends Command
{
    protected static $defaultName = 'app:bulk-import-users';
    private RepositoryInterface $repository;

    public function __construct(RepositoryInterface $repository)
    {
        parent::__construct();
        $this->repository = $repository;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->repository->getPermissionResolver()->setCurrentUserReference(
            $this->repository->getUserService()->loadUser(14)
        );

        $userService = $this->repository->getUserService();
        $contentTypeService = $this->repository->getContentTypeService();
        $userContentType = $contentTypeService->loadContentTypeByIdentifier('user');

        $csv = array_map('str_getcsv', file($input->getArgument('file')));
        $header = array_shift($csv);

        foreach ($csv as $row) {
            $data = array_combine($header, $row);
            try {
                $userGroup = $userService->loadUserGroup((int)$data['group_id']);
                $struct = $userService->newUserCreateStruct(
                    $data['login'], $data['email'], $data['password'],
                    'eng-GB', $userContentType
                );
                $struct->setField('first_name', $data['first_name']);
                $struct->setField('last_name', $data['last_name']);
                $struct->enabled = true;

                $user = $userService->createUser($struct, [$userGroup]);
                $output->writeln("OK: {$data['login']} (ID: {$user->id})");
            } catch (\Exception $e) {
                $output->writeln("FAIL: {$data['login']} -- {$e->getMessage()}");
            }
        }
        return Command::SUCCESS;
    }
}
# Run the bulk import
php bin/console app:bulk-import-users users.csv
# CSV: login,email,password,first_name,last_name,group_id

LDAP Integration

eZ Platform supports LDAP through the Symfony LDAP component:

# config/packages/security.yaml
security:
  providers:
    chain_provider:
      chain:
        providers: [ezplatform, ldap_provider]
    ldap_provider:
      ldap:
        service: Symfony\Component\Ldap\Ldap
        base_dn: dc=example,dc=com
        search_dn: cn=admin,dc=example,dc=com
        search_password: '%env(LDAP_BIND_PASSWORD)%'
        default_roles: ROLE_USER
        uid_key: uid

  firewalls:
    ezplatform_admin:
      provider: chain_provider
      form_login_ldap:
        service: Symfony\Component\Ldap\Ldap
        dn_string: 'uid={username},ou=people,dc=example,dc=com'
# config/services.yaml
services:
  Symfony\Component\Ldap\Ldap:
    arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
  Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
    arguments:
      - host: ldap.example.com
        port: 389
        options:
          protocol_version: 3

When LDAP is configured, users authenticate against the directory and a local eZ user account is created automatically on first login.

SAML SSO (Ibexa DXP)

Ibexa DXP (the commercial successor to eZ Platform) provides built-in SAML support:

# config/packages/ibexa_sso.yaml
ibexa:
  system:
    admin:
      saml:
        idp:
          entity_id: "https://idp.example.com/entity"
          sso_url: "https://idp.example.com/sso"
          certificate: "%kernel.project_dir%/config/certs/idp.crt"
        sp:
          entity_id: "https://your-site.com"
        user_mapping:
          login_attribute: "uid"
          email_attribute: "mail"
          first_name_attribute: "givenName"
          last_name_attribute: "sn"
        default_user_group: 13

Offboarding Checklist

  1. Disable the user -- Set enabled = false to preserve content and audit trail
  2. Move user out of active groups -- Transfer to a "Deactivated" user group
  3. Reassign workflow tasks -- Check for pending editorial workflow items
  4. Review Role assignments -- Remove user-specific Role assignments (not just group-based)
  5. Check personal drafts -- Publish or discard any outstanding drafts
  6. Audit recent changes -- Review the audit log for recent content modifications
  7. Update LDAP/SAML -- Disable the account in your identity provider
  8. Clear sessions -- Remove active sessions: php bin/console ezplatform:sessions:gc