Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Security] Cache voters that will always abstain#43066

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
fabpot merged 1 commit intosymfony:5.4fromjderusse:voter-cache
Oct 28, 2021

Conversation

@jderusse
Copy link
Member

QA
Branch?5.4
Bug fix?no
New feature?yes
Deprecations?no
Tickets-
LicenseMIT
Doc PRtodo

The current implementation of the AccessDecisionManager is to iterate over all the Voters, and stops when the strategy is met.

When an application performs a lot of checks (ie. an Admin shows a list of "blog post" and shows buttons to users that are allowed to "edit", "delete", "publish", ... ie{% if is_granted('blog_delete', item) %}DELETE{% endif %}). In that case, the number of calls to theVoterInterface::vote methods grows exponentially. At some point, adding a new Voter to handle a new case (maybe not related to our blog posts) will have a performance impact

Moreover in most of the time, a voter looks like:

protectedfunctionsupports($attribute,$subject){return\in_array($attribute, ['BLOG_DELETE','BLOG_UPDATE'],true) &&$subjectinstanceof BlogPost;}

We could leverage this, to cache the list of voters that need to be called or can be ignored

In the same waysymfony/serializer provides aCacheableSupportsMethodInterface I suggest to add a newCacheableAbstainVotesInterface that will remember voters that will always abstain vote on a given attribute or a given object's type
When the Voter does not implements the interface OR returnsfalse will provide the current behavior: The voter is always called.

How to use this PR:

class TokenVoterextends Voterimplements CacheableAbstainVotesInterface{publicconstATTRIBUTES = ['UPDATE','DELETE','REGENERATE',    ];publicfunctionwillAlwaysAbstainOnAttribute(string$attribute):bool    {return !\in_array($attribute,self::ATTRIBUTES,true);    }publicfunctionwillAlwaysAbstainOnObjectType(string$objectType):bool    {return !is_a(Token::class,$objectType);    }protectedfunctionsupports($attribute,$subject)    {return\in_array($attribute,self::ATTRIBUTES,true) &&$subjectinstanceof Token;    }protectedfunctionvoteOnAttribute($attribute,$webhookToken,TokenInterface$token)    {...    }}

Results:
For 40 Voters and calling 500 timesisGranted(variousAttributes, variousObjects) it get -40% perf gain !

Opinionated choices in that PR:

  • Only for string attributes: handling all cases (resources, callable, ...) would add too much complexity
  • Only for decision with a single attribute: handling mix of string/null/whatever would add too much complexity

TODO

  • tests (waiting for feedback on the global design of this feature)
  • Doc

dmaicher, Nyholm, and OskarStark reacted with thumbs up emojifbourigault and bastnic reacted with heart emojiderrabus, dmaicher, and sstok reacted with rocket emoji
@ro0NL
Copy link
Contributor

ro0NL commentedSep 16, 2021
edited
Loading

Should we explore dedicated PHP8 class attributes?

IIUC eg.#[VotesOn(attributes, class)] enables compling a warm cache?

@carsonbotcarsonbot changed the titleCache voters that will always abstain[Security] Cache voters that will always abstainSep 16, 2021
@jderussejderusse added this to the5.4 milestoneSep 16, 2021
@nicolas-grekas
Copy link
Member

#[VotesOn(attributes, class)] enables compling a warm cache

I don't know if we need an attribute for that, but what about a method that exposes what a voter cares about@jderusse, instead of the current approach?

@stof
Copy link
Member

@jderusse what about voters that vote only onnull subjects (for instance the RoleVoter) ?

@ro0NL
Copy link
Contributor

ro0NL commentedSep 17, 2021
edited
Loading

@nicolas-grekas i generally like attributes enforcing static/cachable rules to us :) but runtime api also fits yes. As an end user taking care of such issues, an attribute looks elegant IMHO.

@stof
Copy link
Member

An attribute might make the architecture of the AccessDecisionManager harder, if we want to avoid calling Reflection on eachdecide() call. A$voter instanceof CacheableAbstainVotesInterface check is much more efficient than reading attributes at runtime. Having to implement a caching layer for the metadata of the caching layer seems weird to me.

@ro0NL
Copy link
Contributor

ro0NL commentedSep 17, 2021
edited
Loading

Understood. I meant levaraging DI at compile time.

Writing another set of support method implementations already bugs/confuses me somewhat. But it'll be the edge case... WFM.

@jderusse
Copy link
MemberAuthor

what about a method that exposes what a voter cares about instead of the current approach?

Yeah, I hesitated with this approach: but it can not work for voters that use a pattern for attributes: ie RoleVoter =>ROLES_whatever (Which one of the much-used Voters)

@jderusse what about voters that vote only onnull subjects (for instance the RoleVoter) ?

This will work: the current approach is to let voters reporting "I will alwaysabstain for this attribute" and/or "I will alwaysabstain for this object", everything else will always be called.

If voters don't care about subjects (like RoleVoter) they can returnfalse tellingI will **not** always abstain... which meanscall me, in all case.

The code will still be optimized because the voter will not be called when the attribute does not start withROLE_

note: to be accurate RoleVoter does not vote "only on null subject", they vote on "whatever the content of the subject".

Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

LGTM, except the naming :)

What aboutCacheableVoterInterface withsupportsAttribute() andsupportsType() methods?

@jderusse
Copy link
MemberAuthor

Cmments addressed, and tests added

Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

2 comments + fabbot and good to me!

@jderussejderusseforce-pushed thevoter-cache branch 3 times, most recently from33390fe toef8f125CompareOctober 21, 2021 06:48
Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

found some typos, but still 👍 on my side
/cc @symfony/mergers

Copy link
Member

@lyrixxlyrixx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

One minor comment, otherwise 👍🏼 thanks !

@fabpot
Copy link
Member

Thank you@jderusse.

@dkarlovi
Copy link
Contributor

@jderusse@nicolas-grekas

With the renaming of the interface and methods, the semantics seem reversed when you first look at it.

Since the interface is now calledCacheableVoterInterface, the methodssupport*() returningfalse to enable feel inverted. My expectation would be that my voter becomes cacheable if it returnstrue (opts in), just like the serializer equivalent does.

@stof
Copy link
Member

stof commentedNov 4, 2021

support* is not about whether the voter is cacheable, but whether it supports that permission (and so must be called for it)

@jderussejderusse deleted the voter-cache branchNovember 4, 2021 09:31
@dkarlovi
Copy link
Contributor

@stof in that case the interface is misnamed, IMO, the original names seem more appropriate.

@nicolas-grekas
Copy link
Member

The original names were confusing to me. Feels like double negation:CacheableAbstainVotesInterface::willAlwaysAbstainOnAttribute raises a "wtf I am supposed to implement inside that?!?". Supports* method are common and straightforward.

I'm not against renamingCacheableVoterInterface if you have a better proposal. Do you?

@dkarlovi
Copy link
Contributor

if you have a better proposal. Do you?

It depends who gets to define "better". As I said, the weird part here is returningfalse to opt-in to being cacheable.

@jderusse
Copy link
MemberAuthor

thing is, the feature is about improving performance when somethingIS NOT able to process the payload.

Having a negation will always be confusing.

One could useCacheableVoterInterface::doesNotSupportAttribute orUnCacheableVoterInterface::supportAttribute that will always be a cognitive load.

In my opinionsupport* is the way to go: It's not a negation, and consistent with what we do in many other place (including theAbstractVoter)

If we need to change something it should be the name of the interface. Would be happy to make a PR if somebody have a suggestion.

@javiereguiluz
Copy link
Member

javiereguiluz commentedNov 4, 2021
edited by nicolas-grekas
Loading

I'm on a phone so I can't explain on detail, but to me the current implementation is very intuitive because I understand it as follows:

Symfony: Hey voter, do you support ARTICLE_EDIT attribute?
Voter: no (returns FALSE to say that)
Symfony: OK. I will never call you again for this attribute then.

xabbuh, nicolas-grekas, jderusse, dkarlovi, ogizanagi, wouterj, and jvasseur reacted with thumbs up emoji

@dkarlovi
Copy link
Contributor

dkarlovi commentedNov 4, 2021
edited
Loading

This means the voter cacheability is a side-effect of this detection, it's inferred and not directly stated. Your votes is not becoming "cacheable", it's becoming "more capable of explaining what it knows how to work with".. which is the information then used to make it cacheable.

It basically has "extended / detailed supports" support.

ogizanagi reacted with thumbs up emoji

@wouterj
Copy link
Member

I must say that I quite liked thewillAlwaysAbstainOnAttribute() andwillAlwaysAbstainOnObjectType(): it's crystal clear what information is needed from the voter in order to judge whether we should cache it.
I.e. I must only returntrue if I always 100% of the cases abstain on this attribute. We loose this a bit with the current generic name ofsupportsAttribute() imho.

That said, this little nuance can be fulfilled easily by documentation.

dkarlovi and derrabus reacted with thumbs up emoji

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@fabpotfabpotfabpot approved these changes

@nicolas-grekasnicolas-grekasnicolas-grekas approved these changes

@lyrixxlyrixxlyrixx approved these changes

@chalasrchalasrchalasr approved these changes

@wouterjwouterjAwaiting requested review from wouterj

Assignees

No one assigned

Projects

None yet

Milestone

5.4

Development

Successfully merging this pull request may close these issues.

11 participants

@jderusse@ro0NL@nicolas-grekas@stof@fabpot@dkarlovi@javiereguiluz@wouterj@lyrixx@chalasr@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp