<p dir="auto">There seems to be a problem when updating ACL when using object field aces. It is fairy simply to reproduce inside a test controller:</p>
<ol dir="auto">
<li>
<p dir="auto">create a simple domain object:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$demoObj         = new Demo();
$demoObj-&gt;setId(2);

$oid  = ObjectIdentity::fromDomainObject($demoObj);
$fieldName = &quot;name&quot;;"><pre class="notranslate"><code class="notranslate">$demoObj         = new Demo();
$demoObj-&gt;setId(2);

$oid  = ObjectIdentity::fromDomainObject($demoObj);
$fieldName = "name";
</code></pre></div>
</li>
<li>
<p dir="auto">give the object a simple ROLE based ACL:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$aclProvider    = $this-&gt;get('security.acl.provider');
$acl  = $aclProvider-&gt;createAcl($oid);

$roleUser  = new RoleSecurityIdentity('ROLE_USER');
$mask      = new MaskBuilder(4); // 4 = EDIT

$acl-&gt;insertobjectFieldAce($fieldName, $roleUser, $mask-&gt;get());

$aclProvider-&gt;updateAcl($acl);"><pre class="notranslate"><code class="notranslate">$aclProvider    = $this-&gt;get('security.acl.provider');
$acl  = $aclProvider-&gt;createAcl($oid);

$roleUser  = new RoleSecurityIdentity('ROLE_USER');
$mask      = new MaskBuilder(4); // 4 = EDIT

$acl-&gt;insertobjectFieldAce($fieldName, $roleUser, $mask-&gt;get());

$aclProvider-&gt;updateAcl($acl);
</code></pre></div>
</li>
<li>
<p dir="auto">add another ROLE to the ACL:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$acl  = $aclProvider-&gt;findAcl($oid);
$roleUser  = new RoleSecurityIdentity('ROLE_FOO');
$mask      = new MaskBuilder(4); // 4 = EDIT

$acl-&gt;insertobjectFieldAce($fieldName, $roleUser, $mask-&gt;get());
$aclProvider-&gt;updateAcl($acl);"><pre class="notranslate"><code class="notranslate">$acl  = $aclProvider-&gt;findAcl($oid);
$roleUser  = new RoleSecurityIdentity('ROLE_FOO');
$mask      = new MaskBuilder(4); // 4 = EDIT

$acl-&gt;insertobjectFieldAce($fieldName, $roleUser, $mask-&gt;get());
$aclProvider-&gt;updateAcl($acl);
</code></pre></div>
</li>
<li>
<p dir="auto">add one more ROLE to the ACL:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$acl  = $aclProvider-&gt;findAcl($oid);
$roleUser  = new RoleSecurityIdentity('ROLE_BAR');
$mask      = new MaskBuilder(4); // 4 = EDIT

$acl-&gt;insertobjectFieldAce($fieldName, $roleUser, $mask-&gt;get());
$aclProvider-&gt;updateAcl($acl);"><pre class="notranslate"><code class="notranslate">$acl  = $aclProvider-&gt;findAcl($oid);
$roleUser  = new RoleSecurityIdentity('ROLE_BAR');
$mask      = new MaskBuilder(4); // 4 = EDIT

$acl-&gt;insertobjectFieldAce($fieldName, $roleUser, $mask-&gt;get());
$aclProvider-&gt;updateAcl($acl);
</code></pre></div>
</li>
</ol>
<p dir="auto">running step 4 produces the following:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="    An exception occurred while executing 'UPDATE acl_entries SET ace_order
    = 1 WHERE id = 40':

    SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
    '19-22-name-1' for key 
    'UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4'"><pre class="notranslate"><code class="notranslate">    An exception occurred while executing 'UPDATE acl_entries SET ace_order
    = 1 WHERE id = 40':

    SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
    '19-22-name-1' for key 
    'UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4'
</code></pre></div>
<p dir="auto">If you remove the offending index from the database, the update works perfectly (the ace_order), so the problem seems to be that Symfony is not updating the ACL items in the correct order.</p>
<p dir="auto">The above does not affect class field aces.</p>
<p dir="auto">There is a second issue related to the above where the following throws the same execption:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="    $index = 0;
    $acl-&gt;deleteObjectFieldAce($index, $fieldName);
    $acl-&gt;insertObjectFieldAce($fieldName, $roleUser, $mask-&gt;get());
    $aclProvider-&gt;updateAcl($acl);"><pre class="notranslate"><code class="notranslate">    $index = 0;
    $acl-&gt;deleteObjectFieldAce($index, $fieldName);
    $acl-&gt;insertObjectFieldAce($fieldName, $roleUser, $mask-&gt;get());
    $aclProvider-&gt;updateAcl($acl);
</code></pre></div>
<p dir="auto">there is a workaround by doing:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="    $index = 0;
    $acl-&gt;deleteObjectFieldAce($index, $fieldName);
    $aclProvider-&gt;updateAcl($acl);
    $acl-&gt;insertObjectFieldAce($fieldName, $roleUser, $mask-&gt;get());
    $aclProvider-&gt;updateAcl($acl);"><pre class="notranslate"><code class="notranslate">    $index = 0;
    $acl-&gt;deleteObjectFieldAce($index, $fieldName);
    $aclProvider-&gt;updateAcl($acl);
    $acl-&gt;insertObjectFieldAce($fieldName, $roleUser, $mask-&gt;get());
    $aclProvider-&gt;updateAcl($acl);
</code></pre></div>
<p dir="auto">Once again this does not affect class field aces.</p>
<p dir="auto">Problem:</p>
<ol dir="auto">
<li>Session cookie <strong>is not</strong> present (deleted, expired, invalid, etc)</li>
<li>RememberMe cookie <strong>is</strong> present</li>
<li>User is logging out</li>
<li>RememberMe cookie is not deleted</li>
</ol>
<p dir="auto">Alternatively:</p>
<ol dir="auto">
<li>Session cookie <strong>is not</strong> present (deleted, expired, invalid, etc)</li>
<li>User is logging out</li>
<li>No <code class="notranslate">LogoutHandlerInterface</code> implementation is called (because there is no token)</li>
</ol>
<hr>
<p dir="auto">After debugging I see what happens:</p>
<ol dir="auto">
<li><code class="notranslate">security.logout_listener</code> <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php#L298">is created</a></li>
<li><code class="notranslate">security.logout_listener</code> is given <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Component/Security/Http/Logout/LogoutHandlerInterface.php"><code class="notranslate">LogoutHandlerInterface</code>s</a>: <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php#L323">1</a>, <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php#L332">2</a>, <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php#L337">3</a></li>
<li>authentication listeners <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php#L357">are created</a> from factories, including <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php"><code class="notranslate">RememberMeFactory</code></a></li>
<li><code class="notranslate">RememberMeFactory</code> <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php#L51">adds</a> its own <code class="notranslate">LogoutHandlerInterface</code></li>
<li>Security listeners <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php#L359">are merged</a></li>
<li><code class="notranslate">Firewall</code> <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Component/Security/Http/Firewall.php#L67">iterates over listeners</a> and stops on the first giving <code class="notranslate">Response</code>.</li>
</ol>
<p dir="auto">Problem? Listeners are called in this order: <code class="notranslate">ChannelListener</code>, <code class="notranslate">ContextListener</code>, <code class="notranslate">ContainerAwareEventDispatcher</code>, <strong><code class="notranslate">LogoutListener</code></strong>, <code class="notranslate">UsernamePasswordFormAuthenticationListener</code>, <strong><code class="notranslate">RememberMeListener</code></strong>, <code class="notranslate">AnonymousAuthenticationListener</code>, <code class="notranslate">AccessListener</code>.</p>
<p dir="auto">So if the user doesn't have a session cookie, <code class="notranslate">LogoutListener</code> <strong>doesn't call</strong> any <code class="notranslate">LogoutHandlerInterface</code>s, because <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php#L113">there is no token (yet)</a>.</p>
<hr>
<p dir="auto">IMHO <code class="notranslate">LogoutListener</code> should be called last, after other listeners, which might authenticate user before he/she is logged out. Otherwise none of the handlers are called.</p>
<p dir="auto">In my situation it is crucial that <code class="notranslate">RememberMeServices</code> (as <code class="notranslate">LogoutHandlerInterface</code>) + <code class="notranslate">CookieClearingLogoutHandler</code> get called, because Symfony co–exists with non–Symfony app.</p>
<hr>
<p dir="auto">I don't know what's the correct solution, but as a fast hack it could work to:</p>
<ul dir="auto">
<li>override <code class="notranslate">LogoutListener</code> and call <code class="notranslate">LogoutHandlerInterface</code>s manually with a dummy <code class="notranslate">TokenInterface</code> if there is no token yet,</li>
<li>override <code class="notranslate">LogoutListener</code> and do custom actions here (but I believe <code class="notranslate">LogoutHandlerInterface</code>s should be used, as they might contain important logic),</li>
<li>override <a href="https://github.com/symfony/symfony/blob/2.6/src/Symfony/Component/Security/Http/Logout/LogoutSuccessHandlerInterface.php"><code class="notranslate">LogoutSuccessHandlerInterface</code></a>, but same problem as above,</li>
<li>override <code class="notranslate">FirewallMap</code> to change the order of listeners (as it should have already been done in <code class="notranslate">SecurityExtension</code>?).</li>
</ul>