kunstmaan/user-management-bundle
Symfony bundle for user management in Kunstmaan applications, offering reusable user entities, registration, authentication integration, profile editing, password reset, and admin screens to help teams add and manage users with less boilerplate.
This bundle is not a standalone user system — it is the admin UI layer of the Kunstmaan CMS stack. It gives you screens at /{_locale}/admin/settings/ for users, groups and roles, and it delegates the actual security model to FOSUserBundle + Symfony's firewall. If you are not already running kunstmaan/bundles-cms (Symfony 2.x era), there is very little here to leverage.
Prerequisites
friendsofsymfony/user-bundle (~1.3 / ~2.0)kunstmaan/admin-bundle, kunstmaan/admin-list-bundle, kunstmaan/node-bundle (the settings screens are gated on node/admin routing and menus)Install and wire up
composer require kunstmaan/user-management-bundle
// app/AppKernel.php
new Kunstmaan\UserManagementBundle\KunstmaanUserManagementBundle(),
new FOS\UserBundle\FOSUserBundle(),
# app/config/config.yml
fos_user:
db_driver: orm
firewall_name: main
user_class: Kunstmaan\UserManagementBundle\Entity\User
group:
group_class: Kunstmaan\UserManagementBundle\Entity\Group
# app/config/routing.yml
fos_user_security: { resource: "@FOSUserBundle/Resources/config/routing/security.xml" }
kunstmaan_user_management: { resource: "@KunstmaanUserManagementBundle/Resources/config/routing.yml" }
Then run doctrine:schema:update --force (the bundle ships no migrations — the entity schema is your responsibility) and assets:install --symlink web.
Where to look first
Entity/ — User, Group, Role. User and Group extend the FOSUserBundle models; Role is a plain entity that only exists to populate the "assignable roles" list.AdminList/ — UserAdminListConfigurator, GroupAdminListConfigurator, RoleAdminListConfigurator. This is where every column, filter and bulk action lives.Form/ — UserAdminType, GroupAdminType, RoleAdminType. Your extension point for extra profile fields.Controller/ — UserController, GroupController, RoleController. Standard index/add/edit/delete CRUD.Resources/config/services.yml — the service IDs you will override. Read this file before guessing names.First use case
Bootstrap a super admin from the CLI (you cannot log into the UI to create the first one):
app/console fos:user:create admin [email protected] mypassword --super-admin
app/console cache:clear
Log in at /en/admin, go to Settings → Users, edit the user, assign a group and roles, save. Then verify that a second, non-super-admin user is denied access to the settings routes.
Extend the entities, do not edit vendor. The normal day-to-day job is adding fields (phone, locale, avatar, lastLoginIp). Extend Kunstmaan\UserManagementBundle\Entity\User and repoint fos_user.user_class. Important: the base bundle's mapped properties are declared on the parent class, so copy the full mapping into your subclass rather than relying on annotation inheritance — this is the same dance FOSUserBundle documents for its own model.
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Kunstmaan\UserManagementBundle\Entity\User as BaseUser;
/**
* @ORM\Entity
* @ORM\Table(name="app_user")
*/
class User extends BaseUser
{
// re-declare inherited columns here, plus your own:
/** @ORM\Column(type="string", nullable=true) */
protected $phone;
}
Override the admin list configurators to add columns, filters and exports. Subclass the Kunstmaan configurator, adjust it in the constructor (addColumn, addFilter, addAction, addBulkAction), and re-point the service ID (they follow the kunstmaan_user_management.admin_list.configurator.* convention — confirm exact IDs in Resources/config/services.yml).
class UserAdminListConfigurator extends BaseUserAdminListConfigurator
{
public function buildFilters()
{
parent::buildFilters();
$this->addFilter(new StringFilter('phone', 'Phone'));
}
}
kunstmaan_user_management.admin_list.configurator.user:
class: AppBundle\AdminList\UserAdminListConfigurator
# keep the parent's constructor arguments
Override the forms the same way — extend UserAdminType and add your fields. Keep the base plainPassword handling intact; if you ever rewrite it, only call setPassword() when the submitted plain password is non-empty, otherwise every profile edit silently invalidates the user's password.
Treat Role as a dropdown, and security.yml as the truth. The bundle stores role rows in the database purely so editors can tick them in the UI. Hierarchy, isGranted() behaviour and voter logic still live in security.yml. Seed the Role table from a fixture or migration so the list matches your hierarchy, and remember there is no automatic two-way sync — adding a role in security.yml does not make it selectable until someone inserts a row.
# app/config/security.yml — this, not the DB, decides inheritance
security:
role_hierarchy:
ROLE_ADMIN: [ROLE_USER]
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_USER, ROLE_ALLOWED_TO_SWITCH]
Groups are the permission unit for content. In the CMS, Kunstmaan\NodeBundle stores per-node permissions against groups; the user admin screen is where you put people into those groups. The daily workflow is: create group → give it roles → grant it node permissions editor-side → add users to the group. Changing a user's groups is the fastest way to grant/revoke page access, and unlike role edits it takes effect immediately (no re-login needed for node permission checks in most Kunstmaan setups, but role changes do require a fresh login token).
Control the "disabled" axis explicitly. Your User has enabled, locked and expiresAt. The FOSUser UserChecker rejects enabled = false and locked = true; expiresAt is checked by the AccountStatusException path. Pick one convention for "deactivate this person" and stick to it in your own tooling, because the admin form exposes a subset of these and support tickets get confusing when one flag is set and another is not.
Use the CLI for ops rather than hand-editing the DB. The bundle is UI-only; password resets, promotions and provisioning belong in fos:user:* commands or your own console command:
app/console fos:user:change-password jane
app/console fos:user:promote jane ROLE_ADMIN
app/console fos:user:demote jane ROLE_ADMIN
Override templates and translations under app/Resources/KunstmaanUserManagementBundle/views/ and app/Resources/translations/. The settings templates are thin — most cosmetic changes (column ordering, button labels) are easier there than in the configurator.
composer.json constraints will conflict with any modern Symfony, and it pulls in an old FOSUserBundle. Expect to pin symfony/symfony to 2.x and PHP to 5.5/7.0-era versions. If you're on PHP 7.1+ you'll hit deprecations and possibly hard failures on each()/mcrypt usage in the wider stack.doctrine:schema:update is the default path, which means no downgrade and no reviewable diff. Generate a real migration from the schema diff before you ship, especially if you're extending the entity.Role table but not in security.yml can be assigned and will do nothing. Add a fixture that creates both, and add an admin-list column showing the role name so reviewers can spot typos.ROLE_SUPER_ADMIN is not magic unless you configure it. Symfony expands roles only through role_hierarchy; if super admins need to pass isGranted('ROLE_ADMIN') checks, declare it explicitly. Kunstmaan's settings screens check the super-admin role, which is why the first user must be created with --super-admin./admin/settings/; make sure access_control covers the prefix and that the admin menu doesn't render the entry for lower roles. The settings area can change group membership, which is effectively a permission-escalation surface.arguments verbatim. A missing EntityManager or Router argument fails at container compile time with a cryptic InvalidArgumentException, not at request time.createdBy references. Check the CMS's ownership columns before enabling a bulk delete action, and consider the "lock the account" workflow instead.getColumns()/getFilters() rather than guessing.fos_user.encoder is global; if you migrate the encoder algorithm, all existing hashes become unverifiable unless you implement an encoder-aware upgrade-on-login path. Plan this as a project, not a config tweak.cache:clear; the bundle's icons/JS need assets:install --symlink web or the settings pages render unstyled and the JS bulk actions won't wire up.symfony/security-bundle + make:user (Symfony 5+) or a maintained admin CRUD (EasyAdmin, SonataAdmin). Export users, groups and role assignments first; password hashes are portable if you keep the same encoder, which removes the biggest migration pain.How can I help you explore Laravel packages today?