MER-C / wiki-java

A MediaWiki bot framework in Java
GNU Affero General Public License v3.0
66 stars 58 forks source link

Handling of multiple logged-in Wiki instances broken by new JDK cookie manager #157

Closed PeterBowman closed 6 years ago

PeterBowman commented 6 years ago

JDK cookies are used in Wiki.java since https://github.com/MER-C/wiki-java/commit/61eba0fc75c3ffd41280f0e1a3491ff6707e54ae (0.34). The default CookieStore implementation manages cookies on a system-wide manner, which means that URL connections handled by multiple Wiki.java instances are consequently overriden (i.e. each new Wiki.java instance makes all previously created instances re-use its cookies).

I noticed this while running an app that queries two distinct wikis through the same account. My bot user has bot rights on plwiktionary, hence I configure an assertion as shown below:

Wiki plwikt = Wiki.createInstance("pl.wiktionary.org");
plwikt.login("User", "password");
plwikt.setAssertionMode(Wiki.ASSERT_USER | Wiki.ASSERT_BOT);
plwikt.getSiteStatistics(); // fine

Wiki ruwikt = Wiki.createInstance("ru.wiktionary.org");
ruwikt.login("User", "password");
ruwikt.setAssertionMode(Wiki.ASSERT_USER);
ruwikt.getSiteStatistics(); // fine

plwikt.getSiteStatistics(); // error

A java.lang.AssertionError: Assertion that the user has the "bot" right failed. is thrown after the last line. The cookies set by plwikt on initialization and later login are overriden by the ruwikt instance, and in fact, I don't have bot permissions on ruwiktionary.

A similar example will lead to the same result with non-bot accounts: log in with the first Wiki.java instance as a regular user, set the assertion, and don't log in through the second instance.

An alternative approach that helps to reproduce this bug involves using two accounts:

Wiki user1 = Wiki.createInstance("test.wikipedia.org");
user1.login("User1", "password1");
user1.setAssertionMode(Wiki.ASSERT_USER);
user1.edit("Sandbox", "1", "1"); // correct, performed by User1

Wiki user2 = Wiki.createInstance("test.wikipedia.org");
user2.login("User2", "password2");
user2.setAssertionMode(Wiki.ASSERT_USER);
user2.edit("Sandbox", "2", "2"); // correct, performed by User2

user1.edit("Sandbox", "1?", "1?"); // WRONG, performed by User2
PeterBowman commented 6 years ago

I tried https://stackoverflow.com/a/17513786. The assertion issue seems solved, but I get a FailedLoginException in the dual-user sample snippet upon logging in with user2:

result=Aborted
reason=Cannot log in when using MediaWiki\Session\BotPasswordSessionProvider sessions.
PeterBowman commented 6 years ago

Perhaps something like this could restore the old grabCookies mechanism and avoid setting the system-wide cookie handler:

https://github.com/MER-C/wiki-java/blob/bd1b1224c66b99f427e76dc9d0c6f7e61dad1a2e/src/org/wikipedia/Wiki.java#L486

PeterBowman commented 6 years ago

Perhaps something like this could restore the old grabCookies mechanism and avoid setting the system-wide cookie handler:

Done in my fork at https://github.com/PeterBowman/wiki-java/commit/a95d5c87b6dfbaab8ff74960c8f1ffd5e31fb7b0 and seems to solve this. Since it's more of a copy-paste of your former code, @MER-C, I'd like to ask if this solution is robust enough. I'm not familiarized with the reasons behind that grabCookies implementation. Also, I have incidentally restored a previous TODO notice.

MER-C commented 6 years ago

Reproduced with the following:

Wiki testWiki = Wiki.createInstance("test.wikipedia.org");
Wiki enWiki = Wiki.createInstance("en.wikipedia.org");
enWiki.getPageText("Main Page");        
// should still be logged in...
testWiki.setAssertionMode(Wiki.ASSERT_USER);
testWiki.getPageText("Main Page");

I've gone for a slightly modified StackOverflow solution that shoves as much onto the JDK as possible. Please reopen if the commit doesn't fix the bug.

Nirvanchik commented 4 years ago

Thanks PeterBowman for issuing this bug. Updated to 0.34 and my bot suddenly broke because of this.