Problem/Motivation
In previous versions of Flag, we relied on the Session API to give anon users the ability to flag content. One such use is for a site admin to implement an "abuse" or "inappropriate" flag so that users may flag content or abuse anonymously without the possibility of the abuser backtracking who created the flagging or admins inadvertently revealing who reported the offending content.
Proposed resolution
Store the flagging IDs into the session and act accordingly. This allows an anon user to unflag and in parallel other anon users to flag.
Remaining tasks
Write unit tests against the new functionality. A browser test is written.
User interface changes
API changes
Hopefully none.
Comment | File | Size | Author |
---|---|---|---|
#47 | 2475893.47.anonFlagDocs.patch | 1.29 KB | socketwench |
#42 | 2475893_42.patch | 9.85 KB | martin107 |
Comments
Comment #1
joachim CreditAttribution: joachim commentedChanging this to a bug, as it's a regression.
Comment #2
joachim CreditAttribution: joachim commentedI expect we need to add a session ID field to the flagging entity -- so I'd like #2564445: add a 'global' field to Flagging entity to get in first, as the two patches will probably clash.
Comment #3
socketwench CreditAttribution: socketwench at FFW commentedShould we split off the issue to add the session ID to the flagging entity? It wouldn't be a hard issue to implement on it's own.
From what I can tell, instead of relying on the user ID, when the user is anon we instead rely on the session ID?
Comment #4
socketwench CreditAttribution: socketwench at FFW commentedThis is just a step along the way, adding the session ID to flaggings. Apparently, poll does some anon stuff, but by IP and not session ID.
Comment #5
andypostlooks a good for start but needs tests
otoh that could use some deviceID(uuid) to prevent starting sessions
Comment #6
BerdirYeah, I'm not exactly sure about the use cases here. If you rely on the flag count for something, then just a session based approach could get you into trouble. Users just need to open an anonymous window, flag, close and repeat to get as many flaggings as they want.
Comment #7
socketwench CreditAttribution: socketwench at FFW commentedI assumed session IDs simply because they were the first thing mentioned. If we switch to IP addresses, like Poll, we'd have a wealth of existing code from which to base anon flag support.
Comment #8
socketwench CreditAttribution: socketwench at FFW commentedLooking at this, here's what I'm thinking:
Comment #9
socketwench CreditAttribution: socketwench at FFW commentedPartial patch.
Comment #10
joachim CreditAttribution: joachim commentedSo is that using session ID and IP together? Because using just IP will cause problems with people who share an IP, won't it?
Comment #11
ksenzeeMy company's entire headquarters shares one public IP. We would definitely need a session-based solution.
ETA: Obviously whether you want session or IP depends on your use case. For flags that aren't prone to abuse, sessions make more sense. In our case, it would be "I found this content helpful". For abuse-prone flags, you want IP address tracking. Maybe it needs to be two separate issues.
Comment #12
socketwench CreditAttribution: socketwench at FFW commentedHere's a first go at altering the FlaggingStorage controller. I have no idea if this really works.
Comment #13
ksenzeeHere's a working proof of concept for session-based anonymous flagging. I took the patches from #4 and #12 and added session IDs on flagging save and in at least one place where flaggings are retrieved. There may be more.
I have no idea how to get Drupal to write a session cookie for an anonymous session without actually saving data of some kind in the session. This patch saves a 'flag' string to the session which we never look at again. If there's another way to do it, that would be great.
If there's strong feeling that IP-based checking is necessary, that can be added in also, as an additional field on the entity. But I think session support is necessary at a minimum. Without sessions, we have to disallow anonymous unflagging, since IP addresses are shared. Unflagging is a useful feature. ("Whoops! Didn't mean to downvote that post... wait, how do I undo?") It was also available in the D7 version, so losing it would be a regression.
For my personal use case, I really don't want IP-based checking, because I don't look forward to all the bugs that will get filed when people can't see the flag links because they share an IP address with a previous flagger. It would be quite useful for voting-type situations though. Maybe we could add it as an optional per-flag feature?
Comment #15
joachim CreditAttribution: joachim commentedThanks for moving this forwards! :)
> It would be quite useful for voting-type situations though.
I would be inclined to say that if you want voting, use VotingAPI.
Comment #16
chx CreditAttribution: chx at Smartsheet commentedI will in a few hours write another patch which stores the flag into the session instead of storing the session id into the flag. It's not just voting situations, it's stuff like "mark as spam" this of course borders voting -- if N people vote as spam then it might be put in a moderation queue and while on some sites this will not work (because brigading) some others just might.
Comment #17
chx CreditAttribution: chx at Smartsheet commentedThis does not change the stored flags rather stores things in the session. Of course, CSRF checks fail but the policy on https://www.drupal.org/node/2319205 says it's OK not to CSRF check anonymous users and the related core issue #1803712: Allow form tokens to be used on anonymous forms in some cases has seen practically zero progress in four years so I'd rather not wait on it.
Edit: any similarities between FlagServiceProvider and TestServiceProvider are not a coincidence.
Comment #19
chx CreditAttribution: chx at Smartsheet commentedOh look, a modern browser test.
Comment #20
chx CreditAttribution: chx at Smartsheet commentedEven better test: now we switch "browsers" to assert another anonymous user is not affected.
Comment #21
dawehnerI would put a description why we need it. I kinda would have expected to be sort of neutral here.
Do you mind explaining why? I'm just curious here
Oh wow, why is that even needed? Oh is this the way to have different users? Note: getSession takes a name, not sure how to use it though
Comment #22
martin107 CreditAttribution: martin107 commentedComment #23
chx CreditAttribution: chx at Smartsheet commentedAdded comments. I can't get mink session switching to work, I will let the core developers add a session switcher to the base class and I will use it when one is available. Here's my code:
this switches over to the new one just fine but does not switch back properly and I do not know why, posted question on SO. What we have now works and is rather simple.
Comment #24
chx CreditAttribution: chx at Smartsheet commentedComment #25
larowlanNice :)
Maybe $this->sessionName doesn't get rebuilt as well (the stuff we send in the cookie)?
Comment #26
chx CreditAttribution: chx at Smartsheet commentedWe discussed this on IRC and realized that a) sessionName is Drupal's and this is a mink problem b) perhaps the http client is common between the two sessions and if that's so then we are getting to the point where we need to copy initMink into the test except perhaps 2-3 lines and that's simply not worth it.
So: we keep the current code.
Comment #27
socketwench CreditAttribution: socketwench at FFW commentedYikes. Is there no better way to do this?
Comment #28
socketwench CreditAttribution: socketwench at FFW commentedMaybe I don't quite know what's going on here, but wouldn't this open up all access to anon users no matter the route?
Comment #29
joachim CreditAttribution: joachim commented> Yikes. Is there no better way to do this?
What is that actually doing?
Comment #30
chx CreditAttribution: chx at Smartsheet commented#27 and #29: not until #2650812: Container::has() doesn't work for service aliases (consequently, decorating services doesn't work) is fixed and even then I am not 100% what a decorator does with tags. The service provider is a workaround around that bug.
#28: as the class doxygen mentions, there is security policy at https://www.drupal.org/node/2319205 about anonymous CSRF. As for "all access to anon users no matter the route" -- no, it just skips the CSRF access checking. I guess we could add a test that it doesn't -- for now, you can easily verify yourself by removing the grantPermission call from setUp of the supplied test, it will bomb. This CSRF checker is a workaround for the core CSRF access checker being broken.
Comment #31
BerdirCan't we just register our own custom csrf access check that extends from the default implementation and then we use that. We already have such access checks. We could even make csrf checking part of those?
Hm. I guess the problem with that is generating the CSRF token, that wouldn't happen automatically anymore? But since we control the generation of those links we could just do it ourself too.
Less fancy, but easier to understand in the end I think?
Comment #32
andypostCustom csrf should work otoh how that will work for REST?
Comment #33
joachim CreditAttribution: joachim commentedSounds like this needs more thinking about.
At any rate, workarounds needs to be a) documented and b) refer to the core issue they are a workaround for.
Comment #34
chx CreditAttribution: chx at Smartsheet commented> Custom csrf should work otoh how that will work for REST?
REST paths do not call the CSRF checker as far as I am aware?
As I mentioned, even if the alias-decorator bug gets fixed, we'd need the service provider to move the tags. Check DecoratorServicePass in Symfony; it doesn't. Of course if there would be a core issue for fixing or amending the CSRF access checker the service provider and the new access checker could both go away. Definitely the easiest technically but there are other concerns here and so I will not be the one who will file such a core issue.
Comment #35
chx CreditAttribution: chx at Smartsheet commentedComment #36
pheraph CreditAttribution: pheraph commentedComment #37
socketwench CreditAttribution: socketwench at FFW commentedI've been thinking about this patch for the last few days, and my instinct is to turn it down. :-/
I really, really don't like the way we're replacing a core service in the module. I think we're just asking for trouble if another module takes the same tactic, we're out of luck.
I would be more comfortable if we could either wait for this to be fixed in core, or if we could isolate the service replacement into another module or submodule. That would at least give devs the opportunity to disable anon flag support if there's a service conflict.
Comment #38
chx CreditAttribution: chx at Smartsheet commentedI am working as fast on the core patch as I can. It should be ready, actually.
Comment #39
socketwench CreditAttribution: socketwench at FFW commentedComment #40
cedric_aI use the patch in #23 for a small site made for fun and it's working pretty well, but as flag is available for anonymous users, we should prevent GoogleBot and friends from "clicking" a lot on the flag link... so, for the record, when this patch will be reworked, we should add
$render['#attributes']['rel'][] = 'nofollow';
in src/Plugin/ActionLink/AJAXactionLink.php in the buildLink() function.Comment #41
socketwench CreditAttribution: socketwench commentedComment #42
martin107 CreditAttribution: martin107 as a volunteer commentedNo conflicts just automerging..
The nofollow argument (#40) seems a good insight but
before working on this more can someone comment on the state of core changes.
Comment #44
socketwench CreditAttribution: socketwench commented...I still don't like this as a solution, but it's bogging down the entire release while I try to chase some sense of aesthetic purity. No solution here is really going to satisfy me other than a proper fix in core, so instead let's do this and hope we can find a better solution later.
Comment #46
joachim CreditAttribution: joachim commentedI don't think this patch was ready:
This class needs documentation. What does it do?
Could be because it's 6 in the morning, but I can't figure this out.
Needs a comment on it please.
yourmodule?
Also, #2825993: Fix Views support for anonymous flaggings (Unknown column 'flagging_node_field_data.sid') has come up.
Possibly this is just clean-up. However, this approach of storing flagging IDs in the session does mean that we lose the functionality of being able to filter anonymous flaggings by the current user.
Comment #47
socketwench CreditAttribution: socketwench commentedNot seeing the yourmodule reference anywhere in the module.
Docs and comment fixes attached.
Comment #48
joachim CreditAttribution: joachim commentedThanks!
I still don't get the empty for() line... but that's probably moot.
I think we need to revert this patch and rethink this. I don't think storing flaggings in the session is a good idea.
There's the problems with Views, and also #2831393: Unable to unflag() flags for Anon Users when not logged in as a particular Anon User has just come up.
Comment #49
socketwench CreditAttribution: socketwench commentedIf we do that, I'm going to put my foot down and say that this is no longer a 4.0 issue, it's a 4.1 issue.
Comment #50
joachim CreditAttribution: joachim commentedWhat's currently here isn't viable. And do you really want to have to write a migration script to take flaggings from sessions into the regular table? We can't even access those flaggings to delete them, let alone read from them!
(And by not viable, I mean can't satisfy use cases such as 'show me a list of all the flaggings made to this node', or 'tell me the number of people who have flagged this node'.
Comment #51
socketwench CreditAttribution: socketwench commentedNo, not really, that's also not what I'm saying.
Over the holiday I realized it's been a year since D8 was released, and we still don't have a release for this module. Meanwhile, several others blew by and have stable releases. Not much has changed in Flag 4.x in that year, and instead we've been chasing the last 5%-10% of features that were in earlier versions of the module. Features we've labeled "regressions", but core is no longer making it easy to implement. To me, that's a sign that we're on the wrong path.
I think we need to cut features for 4.0, features like this one that are problematic and clumsy. We need to *not* label this as a blocker, unless if we're going to spend another year on this (and we will). I want a release. I'd rather have the module have a stable, working release that people can f-ing use that's missing some of these features, rather than stay in development hell.
The constant pressure of dev hell lead me to just commit this patch in the hope we could sort things out. That was my mistake. If I could do things over, I would have given myself a firm slap and told myself "No, we're not going to have the same functionality and features as we did before. No, we're not going to be able to accurately conjecture if these features are really needed, or how many users are actually effected as long as we stick with dev releases."
Comment #52
joachim CreditAttribution: joachim commentedYup, we're in dev hell, and other modules have released alphas and betas and stables.
Part of the problem I think is that Flag touches a lot of things: it sounds like a simple module on the surface, but there's a lot going on: caching, theming, entities, a complex system of configuration and permissions, and so on.
But also, my impression is that those modules went for an incremental approach to the core version upgrade. They changed the bits that touched core and left the rest for later. That's an approach that's been previously seen to work: make one new major version for the core upgrade, and then a second for the internals. It's why Flag 7.x-2.x still only worked with nodes, users, and comments, and it wasn't until Flag 7.x-3.x that Flag became properly entity-aware. It's an approach that's been proven over the years with other projects and other core version changes.
However, with Flag, there was a ground-up rewrite. I said at the time I thought this was a terrible idea, and nobody listened. (I also said it was too soon to start D8 work, as a lot of energy was spent chasing a moving target, and that I wanted to clean up the 7.x-3.x branch first as preparation work, but nobody listened to that either.)
This is the reason we have a ton of regressions -- the work is simply incomplete. When the D8 code landed here, I was shocked at how much was missing. It honestly looks to me like all the initial work on github was done by people who didn't really know all that much about what Flag does beyond the very basic, and certainly hadn't looked at the Flag 3.x-7.x codebase as a starting point. That's how we've ended up with Flag D8 code that doesn't even have theming, that didn't have any kind of access control, that doesn't support anonymous users. Those aren't extras, they're core features. An incremental approach wouldn't have missed those, because they'd have been there and we'd have seen them in the code. So yes, we've been on the wrong path for a very long time.
If we wanted to drop features that we think are too much maintenance weight, then the time to do that would have been 3 years ago before a single line of D8 code was committed. We would have had time there to run a survey over several months, raise awareness on the project page and at Drupal camps and cons, and get an idea of what's used and what's not. As it stands, we're in the dark.
This particular feature shouldn't be hard. You get a session ID from something, you store it in the flagging. Then you use that when retrieving flaggings for an anonymous user. So I don't this this is a feature that it's reasonable to drop.
Sorry if all this is a bit blunt -- I've been surviving on 2-5 hours of sleep a night for the last three and a half weeks with a new baby.
I don't know where we go from here. There's only one alpha blocker left. The beta blocker and release blocker lists are a fair bit longer. I was hoping to make a dent in those while I am on paternity leave, but I am exhausted and just don't have the brain-power or indeed the free time.
Comment #53
socketwench CreditAttribution: socketwench commentedI have my doubts as to how well that would have worked. I seem to recall Rules or another module doing that and found the results to be very low value for the investment. It's hard to get an accurate reading that doesn't come down to "those that shout the loudest" without very methodical and resource intensive survey methods. That bridge has passed either way.
At least, then, can we cut this as a new issue?
Comment #54
joachim CreditAttribution: joachim commented> At least, then, can we cut this as a new issue?
Sure. My thinking was to revert this patch, but I guess the CRSF stuff is still fine, it's just the storage that is the problem. So I'll commit your docs patch that's here, and we'll move on to a new issue.
Comment #55
martin107 CreditAttribution: martin107 as a volunteer commentedThanks for the long discussion,
It was interesting to see the differing perspectives.
There is now an issue for that ... and I am going to work on it.
Comment #56
socketwench CreditAttribution: socketwench commentedMartin, you give me hope. ^_^
Comment #58
joachim CreditAttribution: joachim commentedCommitted the docs patch.
Comment #59
martin107 CreditAttribution: martin107 as a volunteer commentedI was reminded here
https://www.drupal.org/node/2832106#comment-11806506
#40 is still a good observation, that should not be dropped on the floor.
Comment #60
cedric_aAs suggested by @joachim I created an issue for the 'nofollow' thing : #2832509: Add 'nofollow' on flag link to prevent search bots from visiting it when anonymous flagging will be functional