Helps bot operators adhere to the principle of least privileges.
Grants can now be restricted to allow editing (and other write
operations) for upto 25 listed pages. The page IDs are persisted within
the bp_restrictions field of bot_passwords table, and in the session
metadata.
This restriction is checked only as part of expensive checks in
PermissionManager, since they are not applicable for UI actions.
Bug: T349957
Change-Id: I3d228eb97664d040a160c5b742d9176fdfae9a43
Things like API tests use fake users to make requests. If the test is
not in the Database group, we don't need to save the user's token, as it
would be ignored anyway, so avoid DB access in this scenario.
Bug: T155147
Change-Id: Ic55b79eaebffc6f442db3b446192b30623fbcc0a
* Inappropriate @inheritDoc usage. Arguably all @inheritDoc is
inappropriate but these are the ones PHPStorm flags as misleading
due to the method not being inherited.
* Doc comment type does not match actual argument/return type.
* I replaced "@return void|never" with "@return void" since never means
never, it doesn't make sense for it to be conditional. If a method
can return (even if that is unlikely) then @return contains the type
that it returns. "@return never" means that there is no such type
because the method never returns.
* Incomplete/partial/broken doc tags
Change-Id: Ide86bd6d2b44387f37d234c2b059d6fbc42ec962
The current signature of the various execute methods only takes a
boolean parameter to determine if the session should be safe against
CSRF, but that does not give callers fine-grained control over the
Session object, including setting a specific token.
Also, do not use createNoOpMock in getSession(), since it implies
strong assertions on what methods are called. This way, getSession
can also be used to get a simple mock session that tests may further
manipulate.
Make $csrfSafe parameter of SessionHelperTestTrait::getSession
mandatory. This way, callers are forced to think what makes sense in
each use case. The various methods in HandlerTestTrait now default to
a session that is safe against CSRF. This assumes that most REST
handlers don't care about the session, and that any handler that does
care about the session and where someone needs to test the behaviour
in case of bad/missing token will explicitly provide a Session that
is NOT safe against CSRF.
Typehint the return value of Session(Backend)::getUser so that PHPUnit
will automatically make it return a mock User object even if the method
is not explicitly mocked. Remove a useless PHPUnit assertion -- setting
the return value to be X and then veryfing that is equal to X is a
tautology, and can only fail if the test itself is flawed (as was the
case, since it was using stdClass as the return type for all
methods). Remove the getUser test case altogether, there's no way to
make it work given the DummySessionBackend, and the test isn't that
helpful anyway. More and more methods will have the same issue as soon
as their return value is typehinted.
Follow-up: I2a9215bf909b83564247ded95ecdb4ead0615150
Change-Id: Ic51dc3e7bf47c81f2ac4705308bb9ecd8275bbaf
This was introduced in prep for Multi-DC MediaWiki support many
years ago, but no backend ever supported it, including Kast/RESTBag.
At least for its original use case (SessionBackend) we determined
that session writes are sufficiently rare in practice that the natural
sub-second replication pace and the natural RTT time for browsers
will suffice.
The biggest concern, as I understand it, was around writes that
happen during HTTP post requests and CentralAuth/loginwiki requests
which are routed to the primary DC as per T91820, and use of one-time
tokens which were never stored in Kask (per T278392, these moved
from Redis to mcrouter-primary-dc).
Bug: T270225
Change-Id: Ib29b4090ec84aa0738c087634cef72bfc76a5f71
This reverts commit ec3da4589bebeb46d7f1544dc46f24baec334966.
Caused frequent session loss in the Wikimedia cluster.
Bug: T299193
Bug: T309616
Change-Id: I3a410df88071d72078672cf1b670e81c11b28117
(cherry picked from commit d1a1fcedc9eace8a5f4a8454eff44a7ed898848a)
The getConfig of a ContextSource should only be used, if the
ContextSource is available. Getting the global context just for the
config looks harder to fix/inject as using the MainConfig from
MediaWikiServices
Change-Id: Iaf14bfc7bd68cc315672e1c256887faf87e22542
SessionBackend::resetId() is prone to race conditions with
cookie-based session providers, where MediaWiki receives
a request with the old session and forces the client to log
out. To handle that, add a tombstone mechanism to
SessionBackend, so instead of deleting the old session from
the store on ID reset, it is marked as invalid. Tombstoned
sessions are handled as nonexistent ones, except unpersist()
is not called.
Unlike Iffd69c7f246adff40b07668328a07329440dbd6f this doesn't
prevent overwriting the session if the MediaWiki endpoint calls
persist() or unpersist(), but it is vastly simpler, and very
few endpoints persist the session.
The behavior of SessionManager::loadSessionInfoFromStore()
with a tombstoned session and SessionInfo::forceUse()===true
does not make much sense, but that's a nonsensical scenario
in the first place (it only happens when the session provider
returns true from persistsSessionId() but sets the forceUse
flag which is meant for providers which can't change the
session ID) and we are only really concerned here about
cookie-based sessions anyway.
Bug: T299193
Change-Id: I3a76b67aa51159ebf0195db15cf7c34e00a64a2e
Now largely automated:
VARS=$(grep -o "'[A-Za-z0-9_]*'" includes/MainConfigNames.php | \
tr "\n" '|' | sed "s/|$/\n/;s/'//g")
sed -i -E "s/'($VARS)'/MainConfigNames::\1/g" \
$(grep -ERIl "'($VARS)'" includes/)
Then git add -p with lots of error-prone manual checking. Then
semi-manually add all the necessary "use" lines:
vim $(grep -L 'use MediaWiki\\MainConfigNames;' \
$(git diff --cached --name-only --diff-filter=M HEAD^))
I didn't bother fixing lines that were over 100 characters unless they
were over 120 and triggered phpcs.
Bug: T305805
Change-Id: I74e0ab511abecb276717ad4276a124760a268147
Make phan stricter about conditional variable declaration
Remaining false positive issues are suppressed.
The suppression and the setting change can only be done together
Bug: T259172
Change-Id: I1f200ac37df7448453688bf464a8250c97313e5d
Make phan stricter about null types by setting null_casts_as_any_type to
false (the default in mediawiki-phan-config)
Remaining false positive issues are suppressed.
The suppression and the setting change can only be done together
Bug: T242536
Bug: T301991
Change-Id: I0f295382b96fb3be8037a01c10487d9d591e7e01
In all these cases the property is unconditionally set in
the constructor. The extra initialisation is effectively
dead code and an extra source of errors and confusion.
Change-Id: Icae13390d5ca5c14e2754f3be4eb956dd7f54ac4
User::isValidUserName is deprecated since 1.35 and should be replaced with the UserNameUtils service
Bug: T277398
Change-Id: Iaef995b992e2f38f651453092b23c928479e7d18
The logstash formatter for Monolog 2 doesn't do this automatically
anymore, and it wasn't really correct anyway to rely on a formatter
when we have no guarantee it's going to be used.
Follows up If92d60289fa925d19261ef912e2f2a5d31db31dc.
Bug: T269680
Change-Id: Ia7875346cec95fbc30bc4579ec8b7fb60e911d12
This is micro-optimization of closure code to avoid binding the closure
to $this where it is not needed.
Created by I25a17fb22b6b669e817317a0f45051ae9c608208
Change-Id: I0ffc6200f6c6693d78a3151cb8cea7dce7c21653
For example, documenting the method getUser() with "get the User
object" does not add any information that's not already there.
But I have to read the text first to understand that it doesn't
document anything that's not already obvious from the code.
Some of this is from a time when we had a PHPCS sniff that was
complaining when a line like `@param User $user` doesn't end
with some descriptive text. Some users started adding text like
`@param User $user The User` back then. Let's please remove
this.
Change-Id: I0ea8d051bc732466c73940de9259f87ffb86ce7a
If all goes well, this should cut INFO-level log volume on the
session channel to about a third.
Bug: T264793
Change-Id: I268b57747a1449456df2c98696807c50b05e7147
This is very noisy (logs several times in the same request), but
I'm not sure much can be done about that. It is a flaw in
SessionManager, which does call SessionProvider::persist/unpersist
that many times, and relies on cookie deduplication in WebResponse.
But it should give some idea of when cookies are emitted, and does
not log on normal requests (where no cookies are emitted) so it
shouldn't overload the logging backend.
Bug: T264793
Change-Id: I93733d73af1dfcf539a94b17cf5e4de76cc59748
This patch replaces all usages of @protected in core.
The @protected tag was removed in cases where it was redundant or
contradictory. It has been replaced by @internal where usage outside of
core is not desired, and with @note for cases where use by extensions
is desired, but should be limited.
Bug: T247862
Change-Id: I5da208e5cb4504dde4113afb3a44922fd01325a3
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
Wikimedia\quietCall() is deprecated and AtEase is here for use.
I would have loved to do restoreWarnings() and suppressWarnings()
in this same patch set but will continue the work for a later patch.
Bug: T182273
Change-Id: I43e3a5f378c99b5c40883b35ba133cbd126fc433
These comments do not add anything. I argue they are worse than having
no comments, because I have to read them first to understand they
actually don't explain anything. Removing them makes room for actual
improvements in the future (if needed).
Change-Id: Iee70aad681b3385e9af282d5581c10addbb91ac4
Session writes should be seen in all datacenters so users do not
randomly end up logged on the next page view on a GET request
after login. Sticky DC cookies help, but not for cross domain
redirects or page views.
Change-Id: Id533fa1b867680e6386060efa4878ad1b4638c18
We already save all open SessionBackends when shutdown handlers are run,
which *should* make the Session object destructors that run during
global shutdown not have anything to save. But it can get fooled if the
Session data contains other objects that have already gotten destroyed
during the global shutdown, leading to spurious warnings and errors as
it tries to access partically-destroyed objects.
The solution is to set a flag when we do the shutdown handlers and just
ignore the last gasps from Session::__destruct() that might come after.
Change-Id: Ic3eb0bac2d29a30488c84b6525ad796a7f1c9ce9
Remove "\\" in namespacing. This is a Doxygen compatibility hack but
does not seem needed anymore, Doxygen reads namespaced class names
correctly, see e.g. https://doc.wikimedia.org/mediawiki-core/master/php/classMediaWiki_1_1Services_1_1ServiceContainer.html
PHP IDEs, on the other hand, were broken by the double backslash.
As an unrelated small doc fix, add parameter docs to PermissionError
constructor (parent has different arguments so the inherited
documentation is wrong).
Change-Id: I6da0f512b8c84f65fd20e90e4617108fe6a8fcd2
Clearing the cookies in this case is probably a good idea.
This also clears cookies when a non-persisted session's metadata is
dirty, for parallelism with what happens to persisted sessions.
Bug: T127436
Change-Id: I76897eaac063e5e3c3563398d0f4cb36cf93783b
For anon requests, the call to SessionManager::isUserSessionPrevented(
$this->user->getName() ) is both expensive (because of the need to
sanitize the IP) and pointless, because the session-prevention feature
is intended for named accounts. So short-circuit the check if the user is not
logged in.
Change-Id: I17386b97e229b492723b46db1e1ae16fd4b0fc5a
Now that we dropped support for PHP 5.3.3, we can do this.
The behavior of $session['foo'] when that key doesn't already exist is a
little unexpected (it implicitly assigns null), but it's the best we can
do.
Change-Id: Ibef878867d46591a8bf542139a1719dfec3b83ab
* Use PSR-3 templates and context where applicable
* Add log coverage for exceptional events
Bug: T125452
Change-Id: I8f96fa1c5766c739a21219abcae2dbb76de53e2a