<table role="table">
<thead>
<tr>
<th>Q</th>
<th>A</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bug report?</td>
<td>no</td>
</tr>
<tr>
<td>Feature request?</td>
<td>yes</td>
</tr>
<tr>
<td>BC Break report?</td>
<td>no</td>
</tr>
<tr>
<td>RFC?</td>
<td>yes</td>
</tr>
<tr>
<td>Symfony version</td>
<td>3.3.15</td>
</tr>
</tbody>
</table>
<p dir="auto">Hello, this is a feature request, not a bug report (I guess). To dynamically load form contents, like a select list for an entity or choice type, we have the posibility to use form events and there are some tutorials to do so. When this load involves multiple levels and the events are fired in cascade, this events mechanism, is, to say the less, not developer friendly, I've seen some tutorials but always find them difficult to adapt to my own needs.</p>
<p dir="auto">The method I actually would prefer is handling events with pure Ajax, calling controller methods from javascript that return a JSON with dynamic new values, but here is the problem: if I load a select field (choice or entity) with options that where not present when the form was created I receive some errors:</p>
<blockquote>
<p dir="auto">ConstraintViolation<br>
TransformationFailedException: message: "Unable to reverse value for property path "XXX": The choice "40" does not exist or is not unique"<br>
TransformationFailedException: message: "The choice "40" does not exist or is not unique"</p>
</blockquote>
<p dir="auto">So my request is to allow developers to choose a server based mechanism, like form events, or an ajax based method, allowing new values for choice and entity fields. Maybe what I'm asking crashes completely with the Symfony way of doing things :-P but I don't think I'm the only one who finds cascade form events not developer friendly.</p>
<p dir="auto">Thank you</p>
<table role="table">
<thead>
<tr>
<th>Q</th>
<th>A</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bug report?</td>
<td>no</td>
</tr>
<tr>
<td>Feature request?</td>
<td>yes</td>
</tr>
<tr>
<td>BC Break report?</td>
<td>no</td>
</tr>
<tr>
<td>RFC?</td>
<td>yes</td>
</tr>
<tr>
<td>Symfony version</td>
<td>3.3</td>
</tr>
</tbody>
</table>
<p dir="auto">I'm sure that the Security component is great ... but most Symfony newcomers think that it's an abhorrent monstrosity. Could we please discuss about solutions to reduce its perceived complexity?</p>
<h2 dir="auto">Problem 1 - Low level calls</h2>
<p dir="auto">I think this is the worst problem for newcomers. Symfony forces you to make "low level" calls for all its operations.</p>
<p dir="auto"><strong>Example:</strong> getting the current user (<em>what's a token storage?</em>)</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$user = $this-&gt;get('security.token_storage')-&gt;getToken()-&gt;getUser();"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>user</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security.token_storage'</span>)-&gt;<span class="pl-en">getToken</span>()-&gt;<span class="pl-en">getUser</span>();</pre></div>
<p dir="auto">instead of:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$user = $this-&gt;get('security')-&gt;getUser();

// add this method too in case you need the token
// $user = $this-&gt;get('security')-&gt;getToken();"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>user</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security'</span>)-&gt;<span class="pl-en">getUser</span>();

<span class="pl-c">// add this method too in case you need the token</span>
<span class="pl-c">// $user = $this-&gt;get('security')-&gt;getToken();</span></pre></div>
<p dir="auto"><strong>Example:</strong> checking permissions (<em>who's the authorization checker?</em>)</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;get('security.authorization_checker')-&gt;isGranted('ROLE_ADMIN');
// $this-&gt;get('security.authorization_checker')-&gt;isGranted('ROLE_ADMIN', $subject);"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security.authorization_checker'</span>)-&gt;<span class="pl-en">isGranted</span>(<span class="pl-s">'ROLE_ADMIN'</span>);
<span class="pl-c">// $this-&gt;get('security.authorization_checker')-&gt;isGranted('ROLE_ADMIN', $subject);</span></pre></div>
<p dir="auto">instead of:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;get('security')-&gt;isGranted('ROLE_ADMIN');
// $this-&gt;get('security')-&gt;isGranted('ROLE_ADMIN', $subject);"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security'</span>)-&gt;<span class="pl-en">isGranted</span>(<span class="pl-s">'ROLE_ADMIN'</span>);
<span class="pl-c">// $this-&gt;get('security')-&gt;isGranted('ROLE_ADMIN', $subject);</span></pre></div>
<h2 dir="auto">Problem 2 - Unintuitive behavior</h2>
<p dir="auto"><strong>Example:</strong> defining a role hierarchy simplifies the assignment of roles for users, but complicates the code a lot. You need to deal with the <code class="notranslate">getReachableRoles()</code> method, etc.</p>
<p dir="auto"><strong>Example:</strong> the management of the user types is so confusing. Checking if a user is anonymous is usually done like this:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;get('security.authorization_checker')-&gt;isGranted('IS_AUTHENTICATED_ANONYMOUSLY');"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security.authorization_checker'</span>)-&gt;<span class="pl-en">isGranted</span>(<span class="pl-s">'IS_AUTHENTICATED_ANONYMOUSLY'</span>);</pre></div>
<p dir="auto">First, it's incredibly verbose. Second, it returns <code class="notranslate">true</code> also when the user is NOT strictly anonymous (for example when the user is logged in).</p>
<p dir="auto">Why not add something like this that returns <code class="notranslate">true</code> ONLY when the user is really anonymous (not logged in, not remembered, etc.):</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;get('security')-&gt;isAnonymous();"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security'</span>)-&gt;<span class="pl-en">isAnonymous</span>();</pre></div>
<p dir="auto">The anonymous handling in general is very confusing. For example, having to add the <code class="notranslate">anonymous</code> option to firewalls is always a big "WTF?" in workshops for Symfony newcomers.</p>
<h2 dir="auto">Problem 3 - Common tasks verbosity</h2>
<p dir="auto">Example: encoding a password for the currently logged user</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$user = $this-&gt;get('security.token_storage')-&gt;getToken()-&gt;getUser();
$password = $this-&gt;get('security.password_encoder')-&gt;encodePassword($user, 'foobar');"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>user</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security.token_storage'</span>)-&gt;<span class="pl-en">getToken</span>()-&gt;<span class="pl-en">getUser</span>();
<span class="pl-s1"><span class="pl-c1">$</span>password</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security.password_encoder'</span>)-&gt;<span class="pl-en">encodePassword</span>(<span class="pl-s1"><span class="pl-c1">$</span>user</span>, <span class="pl-s">'foobar'</span>);</pre></div>
<p dir="auto">Instead of:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$password = $this-&gt;get('security')-&gt;encodePassword('foobar');

// in case you want to encode it for other user:
// $password = $this-&gt;get('security')-&gt;encodePassword('foobar', UserInterface $user);"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>password</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security'</span>)-&gt;<span class="pl-en">encodePassword</span>(<span class="pl-s">'foobar'</span>);

<span class="pl-c">// in case you want to encode it for other user:</span>
<span class="pl-c">// $password = $this-&gt;get('security')-&gt;encodePassword('foobar', UserInterface $user);</span></pre></div>
<p dir="auto"><strong>Example:</strong> login a user.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$user = ...
// why does the token need the roles as the 4th arg if I pass the entire user as the 1st arg?
$token = new UsernamePasswordToken($user, $user-&gt;getPassword(), 'main', $user-&gt;getRoles());
$token-&gt;setAuthenticated(true);
$this-&gt;get('security.token_storage')-&gt;setToken($token);
$this-&gt;get('session')-&gt;set('_security_main', serialize($token));
$this-&gt;get('session')-&gt;save();"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>user</span> = <span class="pl-c1">.</span>.<span class="pl-c1">.</span>
<span class="pl-c">// why does the token need the roles as the 4th arg if I pass the entire user as the 1st arg?</span>
<span class="pl-c"></span><span class="pl-s1"><span class="pl-c1">$</span>token</span> = <span class="pl-k">new</span> <span class="pl-v">UsernamePasswordToken</span>(<span class="pl-s1"><span class="pl-c1">$</span>user</span>, <span class="pl-s1"><span class="pl-c1">$</span>user</span>-&gt;<span class="pl-en">getPassword</span>(), <span class="pl-s">'main'</span>, <span class="pl-s1"><span class="pl-c1">$</span>user</span>-&gt;<span class="pl-en">getRoles</span>());
<span class="pl-s1"><span class="pl-c1">$</span>token</span>-&gt;<span class="pl-en">setAuthenticated</span>(<span class="pl-c1">true</span>);
<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security.token_storage'</span>)-&gt;<span class="pl-en">setToken</span>(<span class="pl-s1"><span class="pl-c1">$</span>token</span>);
<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'session'</span>)-&gt;<span class="pl-en">set</span>(<span class="pl-s">'_security_main'</span>, serialize(<span class="pl-s1"><span class="pl-c1">$</span>token</span>));
<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'session'</span>)-&gt;<span class="pl-en">save</span>();</pre></div>
<p dir="auto">instead of:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$user = ...
$this-&gt;get('security')-&gt;login($user);
// support this also: $this-&gt;get('security')-&gt;login($user, $providerKey);"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>user</span> = <span class="pl-c1">.</span>.<span class="pl-c1">.</span>
<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'security'</span>)-&gt;<span class="pl-en">login</span>(<span class="pl-s1"><span class="pl-c1">$</span>user</span>);
<span class="pl-c">// support this also: $this-&gt;get('security')-&gt;login($user, $providerKey);</span></pre></div>
<h2 dir="auto">Problem 4 - Roles, attributes</h2>
<ul dir="auto">
<li>The concept of <em>"security attributes"</em> is hard to understand for a lot of newcomers. If they were called <em>"security permissions"</em>, anyone could understand them even without an explanation.</li>
<li>Having roles separated from attributes is always confusing. Are roles attributes? Are a special type of attributes? Are something different but related? Is <code class="notranslate">IS_AUTHENTICATED_ANONYMOUSLY</code> a role or not?</li>
</ul>
<p dir="auto">We could make everything "security permissions" and forget about roles.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// Before:
@Security(&quot;has_role('ROLE_ADMIN')&quot;)
@Security(&quot;is_granted('EDIT_PRODUCT')&quot;)

// After:
@Security(&quot;is_granted('ROLE_ADMIN')&quot;)
@Security(&quot;is_granted('EDIT_PRODUCT')&quot;)
// Alternative
@Security(&quot;is_granted('ROLE_ADMIN') and is_granted('EDIT_PRODUCT')&quot;)"><pre class="notranslate"><span class="pl-c">// Before:</span>
@<span class="pl-v">Security</span>("<span class="pl-s">has_role('ROLE_ADMIN')</span>")
@<span class="pl-v">Security</span>("<span class="pl-s">is_granted('EDIT_PRODUCT')</span>")

<span class="pl-c">// After:</span>
@<span class="pl-v">Security</span>("<span class="pl-s">is_granted('ROLE_ADMIN')</span>")
@<span class="pl-v">Security</span>("<span class="pl-s">is_granted('EDIT_PRODUCT')</span>")
<span class="pl-c">// Alternative</span>
@<span class="pl-v">Security</span>("<span class="pl-s">is_granted('ROLE_ADMIN') and is_granted('EDIT_PRODUCT')</span>")</pre></div>
<p dir="auto">Maintaining the <code class="notranslate">ROLE_</code> prefix would be a good idea for most apps, but if they were permissions, people could remove it if needed. Example: "does the user have the <code class="notranslate">ADMIN</code> permission?" instead of "does the user has the <code class="notranslate">ROLE_ADMIN</code> role?".</p>
<h2 dir="auto">Final remarks</h2>
<p dir="auto">I'm not proposing to remove the low level security calls. I'm not asking to hide Security features. I'm not asking to remove developer's control of Security component. I'm just asking to:</p>
<ul dir="auto">
<li>Make people <strong>work less</strong> and <strong>learn less things</strong> to make the common security tasks.</li>
<li>Allow people to go deep into low level functions if they need to (exactly the same as today).</li>
</ul>
<h2 dir="auto">References</h2>
<p dir="auto">A while ago I published a proof-of-concept bundle with some of these ideas: <a href="https://github.com/EasyCorp/easy-security-bundle">EasySecurityBundle</a>. Don't use it in real applications! I don't know if it's safe and it's not optimized for performance. It was just a proof of concept to see if this idea is doable.</p>