New macro {ifAuthorized} to check links in advance
- juzna.cz
- Member | 248
TL;DR
1/ classic
Add new macro {ifAuthorized destination [,] params}
to check if
user will be authorized for given action (i.e. the link won't cause 403 –
ForbiddenRequestException)/
{ifAuthorized like! $item->id}
<a n:href="like! $item->id">Like</a>
{/ifAuthorized}
2/ smart (and little bit magic)
{ifAuthorized}
to check all link within this block
{ifAuthorized} {* magic happens here *}
<a n:href="like! $item->id">Like</a> {* this link will be checked *}
{/ifAuthorized}
or
<a n:ifAuthorized n:href="like! $item->id">Like</a> {* nice, huh? *}
Le introduction
You have some actions/signals only for authorized
users; e.g. like signal similar to what we know from facebook. This is
validated in Presenter::checkRequirements
(which needs refacroting)
and typically uses method annotations like @User
(implemeneted in
nette) or @User(role=admin)
(possible extension). This is good and
secure.
Now, you have a template where you display an item from catalog, and display the like link only if user is authorized to use it. You have to write the autorization rules again in the template.
{if $user->isLoggedIn()}
<a n:href="like! $item->id">Like</a>
{/if}
or
<a n:if="$user->isLoggedIn()" n:href="like! $item->id">Like</a>
So far, it's good.
Le problem
Now you want to make a change, that the like signal will be allowed only for special users, lets say with role editor or admin. You update annotation of given signal, and you have to update all the templates which point to this signal. What if the conditions become more complicated? And what if you forget it somewhere? You've got the same information in far many places now. That's not DRY.
{if $user->isInRole('editor') || $user->isInRole('admin')}
<a n:href="like! $item->id">Like</a>
{/if}
or
<a n:if="$user->isInRole('editor') || $user->isInRole('admin')" n:href="like! $item->id">Like</a>
Not so nice anymore :/
Le {ifAuthorized ...}
macro
Ok, let's make it nicer. We can have a new macro which would create the link and validate it's authorization requirements. If the target action won't be allowed, we simply won't show the link.
And this will be completely modular, all the checks will be done by the same
method (checkRequirements
) as if the request would get really
executed. Thus you don't need to implement it twice. All the authorization
information will be in one place, in annotations of
signals/actions.
{ifAuthorized like! $item->id}
<a n:href="like! $item->id">Like</a>
{/ifAuthorized}
or
<a n:ifAuthorized="like! $item->id" n:href="like! $item->id">Like</a>
Much nicer, huh? If the user is authorized to like an item, then we will show the link to like the item. Clear, straightforward, not repeating the authorization conditions.
But, we still repeat some code: ` like! $item->id` appears twice, that is still not nice.
Le smart {ifAuthorized}
macro
Okay, let's omit the duplicated information. Macro will automatically check the link within. It may seem a little bit magical now, so be careful.
{ifAuthorized}
<a n:href="like! $item->id">Like</a>
{/ifAuthorized}
or
<a n:ifAuthorized n:href="like! $item->id">Like</a>
Hmm, I don't have to repeat the links twice now, great. But it seems magical: the code now says “if authorized” and you may reply “yeah, authorized what???”. It's upto you, if you don't want to repeat the conditions, you don't have to. But you surely can write them twice, if you don't like the magic.
Le more advanced stuff
You can add conditions not only to an anchor, but to any piece of code. If you love wrapping your anchors like christmas presentrs, you'll use:
<div n:ifAuthorized>
<span>
<span>
<a n:href="like! $item->id">Like</a>
</span>
</span>
</div>
You can use it also for other elements, not just links (if it makes sence somewhere):
<button n:ifAuthorized="like! $item->id" onclick="handleThisWithJavaScript();">Like</button>
here of course, you have to write down the target action since it's not used anywhere within the element.
Le implementation
First of all, we need method checkRequirements()
to do what
it says it does.
Then, there is a pull
request and works fine in my project.
- Schmutzka
- Moderator | 1114
Fantastic!
Le problem could be solved by something like this (dunno how/if possible to implement):
{* detect if array 'roles' found, if so, it contains allowed roles *}
{allowed roles => [editor, admin]}
<a n:href="like! $item->id">Like</a>
{/allowed}
Last edited by Schmutzka (2012-03-25 04:03)