3 use MediaWiki\Api\ApiUsageException
;
4 use MediaWiki\MainConfigNames
;
5 use MediaWiki\Page\File\FileDeleteForm
;
6 use MediaWiki\Tests\Api\ApiTestCase
;
7 use MediaWiki\Title\Title
;
8 use MediaWiki\User\User
;
9 use Wikimedia\TestingAccessWrapper
;
16 * @covers \UploadFromUrl
18 class UploadFromUrlTest
extends ApiTestCase
{
24 protected function setUp(): void
{
26 $this->user
= $this->getTestSysop()->getUser();
28 $this->overrideConfigValues( [
29 MainConfigNames
::EnableUploads
=> true,
30 MainConfigNames
::AllowCopyUploads
=> true,
32 $this->setGroupPermissions( 'sysop', 'upload_by_url', true );
34 if ( $this->getServiceContainer()->getRepoGroup()->getLocalRepo()
35 ->newFile( 'UploadFromUrlTest.png' )->exists()
37 $this->deleteFile( 'UploadFromUrlTest.png' );
40 $this->installMockHttp();
44 * Ensure that the job queue is empty before continuing
46 public function testClearQueue() {
47 $jobQueueGroup = $this->getServiceContainer()->getJobQueueGroup();
48 $job = $jobQueueGroup->pop();
50 $job = $jobQueueGroup->pop();
52 $this->assertFalse( $job );
55 public function testIsAllowedHostEmpty() {
56 $this->overrideConfigValues( [
57 MainConfigNames
::CopyUploadsDomains
=> [],
58 MainConfigNames
::CopyUploadAllowOnWikiDomainConfig
=> false,
61 $this->assertTrue( UploadFromUrl
::isAllowedHost( 'https://foo.bar' ) );
64 public function testIsAllowedHostDirectMatch() {
65 $this->overrideConfigValues( [
66 MainConfigNames
::CopyUploadsDomains
=> [
70 MainConfigNames
::CopyUploadAllowOnWikiDomainConfig
=> false,
73 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://example.com' ) );
75 $this->assertTrue( UploadFromUrl
::isAllowedHost( 'https://foo.baz' ) );
76 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://.foo.baz' ) );
78 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://example.baz' ) );
79 $this->assertTrue( UploadFromUrl
::isAllowedHost( 'https://bar.example.baz' ) );
82 public function testIsAllowedHostLastWildcard() {
83 $this->overrideConfigValues( [
84 MainConfigNames
::CopyUploadsDomains
=> [
87 MainConfigNames
::CopyUploadAllowOnWikiDomainConfig
=> false,
90 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://baz' ) );
91 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://foo.example' ) );
92 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://foo.example.baz' ) );
93 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://foo/bar.baz' ) );
95 $this->assertTrue( UploadFromUrl
::isAllowedHost( 'https://foo.baz' ) );
96 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://subdomain.foo.baz' ) );
99 public function testIsAllowedHostWildcardInMiddle() {
100 $this->overrideConfigValues( [
101 MainConfigNames
::CopyUploadsDomains
=> [
104 MainConfigNames
::CopyUploadAllowOnWikiDomainConfig
=> false,
107 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://foo.baz' ) );
108 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://foo.bar.bar.baz' ) );
109 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://foo.bar.baz.baz' ) );
110 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://foo.com/.baz' ) );
112 $this->assertTrue( UploadFromUrl
::isAllowedHost( 'https://foo.example.baz' ) );
113 $this->assertTrue( UploadFromUrl
::isAllowedHost( 'https://foo.bar.baz' ) );
116 public function testOnWikiDomainConfigEnabled() {
117 $this->overrideConfigValues( [
118 MainConfigNames
::CopyUploadsDomains
=> [ 'example.com' ],
119 MainConfigNames
::CopyUploadAllowOnWikiDomainConfig
=> true,
122 $messageContent = "example.org # this is a comment\n# this too is commented foo.example.com\nexample.net";
123 $mock = $this->createMock( MessageCache
::class );
124 $mock->method( 'get' )->willReturn( $messageContent );
125 $this->setService( 'MessageCache', $mock );
128 [ 'example.com', 'example.org', 'example.net' ],
129 TestingAccessWrapper
::newFromClass( UploadFromUrl
::class )->getAllowedHosts()
132 $this->assertTrue( UploadFromUrl
::isAllowedHost( 'https://example.com' ) );
133 $this->assertTrue( UploadFromUrl
::isAllowedHost( 'https://example.org' ) );
134 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://foo.example.com' ) );
137 public function testOnWikiDomainConfigDisabled() {
138 $this->overrideConfigValues( [
139 MainConfigNames
::CopyUploadsDomains
=> [ 'example.com' ],
140 MainConfigNames
::CopyUploadAllowOnWikiDomainConfig
=> false,
143 $mock = $this->createMock( MessageCache
::class );
144 $mock->expects( $this->never() )->method( 'get' );
145 $this->setService( 'MessageCache', $mock );
149 TestingAccessWrapper
::newFromClass( UploadFromUrl
::class )->getAllowedHosts()
152 $this->assertTrue( UploadFromUrl
::isAllowedHost( 'https://example.com' ) );
153 $this->assertFalse( UploadFromUrl
::isAllowedHost( 'https://example.org' ) );
157 * @depends testClearQueue
159 public function testSetupUrlDownload( $data ) {
160 $token = $this->user
->getEditToken();
164 $this->doApiRequest( [
165 'action' => 'upload',
167 } catch ( ApiUsageException
$e ) {
169 $this->assertApiErrorCode( 'missingparam', $e );
171 $this->assertTrue( $exception, "Got exception" );
175 $this->doApiRequest( [
176 'action' => 'upload',
179 } catch ( ApiUsageException
$e ) {
181 $this->assertApiErrorCode( 'missingparam', $e );
183 $this->assertTrue( $exception, "Got exception" );
187 $this->doApiRequest( [
188 'action' => 'upload',
189 'url' => 'http://www.example.com/test.png',
192 } catch ( ApiUsageException
$e ) {
194 $this->assertApiErrorCode( 'nofilename', $e );
196 $this->assertTrue( $exception, "Got exception" );
198 $this->getServiceContainer()->getUserGroupManager()->removeUserFromGroup( $this->user
, 'sysop' );
201 $this->doApiRequest( [
202 'action' => 'upload',
203 'url' => 'http://www.example.com/test.png',
204 'filename' => 'UploadFromUrlTest.png',
207 } catch ( ApiUsageException
$e ) {
208 $this->assertApiErrorCode( 'permissiondenied', $e );
211 $this->assertTrue( $exception, "Got exception" );
214 private function assertUploadOk( UploadBase
$upload ) {
215 $verificationResult = $upload->verifyUpload();
217 if ( $verificationResult['status'] !== UploadBase
::OK
) {
219 'Upload verification returned ' . $upload->getVerificationErrorCode(
220 $verificationResult['status']
227 * @depends testClearQueue
229 public function testSyncDownload( $data ) {
230 $file = __DIR__
. '/../../data/upload/png-plain.png';
231 $this->installMockHttp( file_get_contents( $file ) );
233 $this->getServiceContainer()->getUserGroupManager()->addUserToGroup( $this->user
, 'users' );
234 $data = $this->doApiRequestWithToken( [
235 'action' => 'upload',
236 'filename' => 'UploadFromUrlTest.png',
237 'url' => 'http://upload.wikimedia.org/wikipedia/mediawiki/b/bc/Wiki.png',
238 'ignorewarnings' => true,
241 $this->assertEquals( 'Success', $data[0]['upload']['result'] );
242 $this->deleteFile( 'UploadFromUrlTest.png' );
247 protected function deleteFile( $name ) {
248 $t = Title
::newFromText( $name, NS_FILE
);
249 $this->assertTrue( $t->exists(), "File '$name' exists" );
251 if ( $t->exists() ) {
252 $file = $this->getServiceContainer()->getRepoGroup()
253 ->findFile( $name, [ 'ignoreRedirect' => true ] );
255 FileDeleteForm
::doDelete( $t, $file, $empty, "none", true, $this->user
);
256 $page = $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( $t );
257 $this->deletePage( $page );
259 $t = Title
::newFromText( $name, NS_FILE
);
261 $this->assertFalse( $t->exists(), "File '$name' was deleted" );
264 public function testUploadFromUrl() {
265 $file = __DIR__
. '/../../data/upload/png-plain.png';
266 $this->installMockHttp( file_get_contents( $file ) );
268 $upload = new UploadFromUrl();
269 $upload->initialize( 'Test.png', 'http://www.example.com/test.png' );
270 $status = $upload->fetchFile();
272 $this->assertStatusOK( $status );
273 $this->assertUploadOk( $upload );
276 public function testUploadFromUrlWithRedirect() {
277 $file = __DIR__
. '/../../data/upload/png-plain.png';
278 $this->installMockHttp( [
279 // First response is a redirect
280 $this->makeFakeHttpRequest(
283 [ 'Location' => 'http://static.example.com/files/test.png' ]
285 // Second response is a file
286 $this->makeFakeHttpRequest(
287 file_get_contents( $file )
291 $upload = new UploadFromUrl();
292 $upload->initialize( 'Test.png', 'http://www.example.com/test.png' );
293 $status = $upload->fetchFile();
295 $this->assertStatusOK( $status );
296 $this->assertUploadOk( $upload );
299 public function testUploadFromUrlCacheKey() {
300 // Test we get back a properly formatted sha1 key out
301 $key = UploadFromUrl
::getCacheKey( [ 'filename' => 'test.png', 'url' => 'https://example.com/example.png' ] );
302 $this->assertNotEmpty( $key );
303 $this->assertMatchesRegularExpression( "/^[0-9a-f]{40}$/", $key );
306 public function testUploadFromUrlCacheKeyMissingParam() {
307 $this->assertSame( "", UploadFromUrl
::getCacheKey( [] ) );