mirror of
				https://github.com/BookStackApp/BookStack.git
				synced 2025-11-03 02:13:16 +03:00 
			
		
		
		
	Merge branch 'v21.04.x'
This commit is contained in:
		@@ -19,10 +19,37 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class SocialAuthService
 | 
					class SocialAuthService
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The core socialite library used.
 | 
				
			||||||
 | 
					     * @var Socialite
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    protected $socialite;
 | 
					    protected $socialite;
 | 
				
			||||||
    protected $socialAccount;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $validSocialDrivers = ['google', 'github', 'facebook', 'slack', 'twitter', 'azure', 'okta', 'gitlab', 'twitch', 'discord'];
 | 
					    /**
 | 
				
			||||||
 | 
					     * The default built-in social drivers we support.
 | 
				
			||||||
 | 
					     * @var string[]
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected $validSocialDrivers = [
 | 
				
			||||||
 | 
					        'google',
 | 
				
			||||||
 | 
					        'github',
 | 
				
			||||||
 | 
					        'facebook',
 | 
				
			||||||
 | 
					        'slack',
 | 
				
			||||||
 | 
					        'twitter',
 | 
				
			||||||
 | 
					        'azure',
 | 
				
			||||||
 | 
					        'okta',
 | 
				
			||||||
 | 
					        'gitlab',
 | 
				
			||||||
 | 
					        'twitch',
 | 
				
			||||||
 | 
					        'discord'
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Callbacks to run when configuring a social driver
 | 
				
			||||||
 | 
					     * for an initial redirect action.
 | 
				
			||||||
 | 
					     * Array is keyed by social driver name.
 | 
				
			||||||
 | 
					     * Callbacks are passed an instance of the driver.
 | 
				
			||||||
 | 
					     * @var array<string, callable>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected $configureForRedirectCallbacks = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * SocialAuthService constructor.
 | 
					     * SocialAuthService constructor.
 | 
				
			||||||
@@ -39,7 +66,7 @@ class SocialAuthService
 | 
				
			|||||||
    public function startLogIn(string $socialDriver): RedirectResponse
 | 
					    public function startLogIn(string $socialDriver): RedirectResponse
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $driver = $this->validateDriver($socialDriver);
 | 
					        $driver = $this->validateDriver($socialDriver);
 | 
				
			||||||
        return $this->getSocialDriver($driver)->redirect();
 | 
					        return $this->getDriverForRedirect($driver)->redirect();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -49,7 +76,7 @@ class SocialAuthService
 | 
				
			|||||||
    public function startRegister(string $socialDriver): RedirectResponse
 | 
					    public function startRegister(string $socialDriver): RedirectResponse
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $driver = $this->validateDriver($socialDriver);
 | 
					        $driver = $this->validateDriver($socialDriver);
 | 
				
			||||||
        return $this->getSocialDriver($driver)->redirect();
 | 
					        return $this->getDriverForRedirect($driver)->redirect();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -227,7 +254,7 @@ class SocialAuthService
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Provide redirect options per service for the Laravel Socialite driver
 | 
					     * Provide redirect options per service for the Laravel Socialite driver
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function getSocialDriver(string $driverName): Provider
 | 
					    protected function getDriverForRedirect(string $driverName): Provider
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $driver = $this->socialite->driver($driverName);
 | 
					        $driver = $this->socialite->driver($driverName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -238,6 +265,10 @@ class SocialAuthService
 | 
				
			|||||||
            $driver->with(['resource' => 'https://graph.windows.net']);
 | 
					            $driver->with(['resource' => 'https://graph.windows.net']);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (isset($this->configureForRedirectCallbacks[$driverName])) {
 | 
				
			||||||
 | 
					            $this->configureForRedirectCallbacks[$driverName]($driver);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $driver;
 | 
					        return $driver;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -248,12 +279,19 @@ class SocialAuthService
 | 
				
			|||||||
     * within the `Config/services.php` file.
 | 
					     * within the `Config/services.php` file.
 | 
				
			||||||
     * Handler should be a Class@method handler to the SocialiteWasCalled event.
 | 
					     * Handler should be a Class@method handler to the SocialiteWasCalled event.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function addSocialDriver(string $driverName, array $config, string $socialiteHandler)
 | 
					    public function addSocialDriver(
 | 
				
			||||||
    {
 | 
					        string $driverName,
 | 
				
			||||||
 | 
					        array $config,
 | 
				
			||||||
 | 
					        string $socialiteHandler,
 | 
				
			||||||
 | 
					        callable $configureForRedirect = null
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
        $this->validSocialDrivers[] = $driverName;
 | 
					        $this->validSocialDrivers[] = $driverName;
 | 
				
			||||||
        config()->set('services.' . $driverName, $config);
 | 
					        config()->set('services.' . $driverName, $config);
 | 
				
			||||||
        config()->set('services.' . $driverName . '.redirect', url('/login/service/' . $driverName . '/callback'));
 | 
					        config()->set('services.' . $driverName . '.redirect', url('/login/service/' . $driverName . '/callback'));
 | 
				
			||||||
        config()->set('services.' . $driverName . '.name', $config['name'] ?? $driverName);
 | 
					        config()->set('services.' . $driverName . '.name', $config['name'] ?? $driverName);
 | 
				
			||||||
        Event::listen(SocialiteWasCalled::class, $socialiteHandler);
 | 
					        Event::listen(SocialiteWasCalled::class, $socialiteHandler);
 | 
				
			||||||
 | 
					        if (!is_null($configureForRedirect)) {
 | 
				
			||||||
 | 
					            $this->configureForRedirectCallbacks[$driverName] = $configureForRedirect;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,9 +53,9 @@ class ThemeService
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @see SocialAuthService::addSocialDriver
 | 
					     * @see SocialAuthService::addSocialDriver
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function addSocialDriver(string $driverName, array $config, string $socialiteHandler)
 | 
					    public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, callable $configureForRedirect = null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $socialAuthService = app()->make(SocialAuthService::class);
 | 
					        $socialAuthService = app()->make(SocialAuthService::class);
 | 
				
			||||||
        $socialAuthService->addSocialDriver($driverName, $config, $socialiteHandler);
 | 
					        $socialAuthService->addSocialDriver($driverName, $config, $socialiteHandler, $configureForRedirect);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -140,12 +140,13 @@ class ImageService
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $storage->put($path, $data);
 | 
					        $storage->put($path, $data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set visibility if using s3 without an endpoint set.
 | 
					        // Set visibility when a non-AWS-s3, s3-like storage option is in use.
 | 
				
			||||||
        // Done since this call can break s3-like services but desired for actual
 | 
					        // Done since this call can break s3-like services but desired for other image stores.
 | 
				
			||||||
        // AWS s3 usage. Attempting to set ACL during above put request requires
 | 
					        // Attempting to set ACL during above put request requires different permissions
 | 
				
			||||||
        // different permissions hence would technically be a breaking change.
 | 
					        // hence would technically be a breaking change for actual s3 usage.
 | 
				
			||||||
        $usingS3 = strtolower(config('filesystems.images')) === 's3';
 | 
					        $usingS3 = strtolower(config('filesystems.images')) === 's3';
 | 
				
			||||||
        if ($usingS3 && is_null(config('filesystems.disks.s3.endpoint'))) {
 | 
					        $usingS3Like = $usingS3 && !is_null(config('filesystems.disks.s3.endpoint'));
 | 
				
			||||||
 | 
					        if (!$usingS3Like) {
 | 
				
			||||||
            $storage->setVisibility($path, 'public');
 | 
					            $storage->setVisibility($path, 'public');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -95,4 +95,18 @@ Theme::listen(ThemeEvents::APP_BOOT, function($app) {
 | 
				
			|||||||
        'name' => 'Reddit',
 | 
					        'name' => 'Reddit',
 | 
				
			||||||
    ], '\SocialiteProviders\Reddit\RedditExtendSocialite@handle');
 | 
					    ], '\SocialiteProviders\Reddit\RedditExtendSocialite@handle');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In some cases you may need to customize the driver before it performs a redirect. 
 | 
				
			||||||
 | 
					This can be done by providing a callback as a fourth parameter like so:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```php
 | 
				
			||||||
 | 
					Theme::addSocialDriver('reddit', [
 | 
				
			||||||
 | 
					    'client_id' => 'abc123',
 | 
				
			||||||
 | 
					    'client_secret' => 'def456789',
 | 
				
			||||||
 | 
					    'name' => 'Reddit',
 | 
				
			||||||
 | 
					], '\SocialiteProviders\Reddit\RedditExtendSocialite@handle', function($driver) {
 | 
				
			||||||
 | 
					    $driver->with(['prompt' => 'select_account']);
 | 
				
			||||||
 | 
					    $driver->scopes(['open_id']);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
<?php namespace Tests;
 | 
					<?php namespace Tests;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use BookStack\Auth\Access\SocialAuthService;
 | 
					 | 
				
			||||||
use BookStack\Auth\User;
 | 
					use BookStack\Auth\User;
 | 
				
			||||||
use BookStack\Entities\Models\Page;
 | 
					use BookStack\Entities\Models\Page;
 | 
				
			||||||
use BookStack\Entities\Tools\PageContent;
 | 
					use BookStack\Entities\Tools\PageContent;
 | 
				
			||||||
@@ -149,7 +148,7 @@ class ThemeTest extends TestCase
 | 
				
			|||||||
        $this->setSettings(['registration-enabled' => 'true']);
 | 
					        $this->setSettings(['registration-enabled' => 'true']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $user = factory(User::class)->make();
 | 
					        $user = factory(User::class)->make();
 | 
				
			||||||
        $this->post('/register', ['email' => $user->email, 'name' => $user->name,  'password' => 'password']);
 | 
					        $this->post('/register', ['email' => $user->email, 'name' => $user->name, 'password' => 'password']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->assertCount(2, $args);
 | 
					        $this->assertCount(2, $args);
 | 
				
			||||||
        $this->assertEquals('standard', $args[0]);
 | 
					        $this->assertEquals('standard', $args[0]);
 | 
				
			||||||
@@ -184,6 +183,28 @@ class ThemeTest extends TestCase
 | 
				
			|||||||
        $loginResp->assertSee('Super Cat Name');
 | 
					        $loginResp->assertSee('Super Cat Name');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function test_add_social_driver_allows_a_configure_for_redirect_callback_to_be_passed()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Theme::addSocialDriver(
 | 
				
			||||||
 | 
					            'discord',
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                'client_id' => 'abc123',
 | 
				
			||||||
 | 
					                'client_secret' => 'def456',
 | 
				
			||||||
 | 
					                'name' => 'Super Cat Name',
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            'SocialiteProviders\Discord\DiscordExtendSocialite@handle',
 | 
				
			||||||
 | 
					            function ($driver) {
 | 
				
			||||||
 | 
					                $driver->with(['donkey' => 'donut']);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $loginResp = $this->get('/login/service/discord');
 | 
				
			||||||
 | 
					        $redirect = $loginResp->headers->get('location');
 | 
				
			||||||
 | 
					        $this->assertStringContainsString('donkey=donut', $redirect);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected function usingThemeFolder(callable $callback)
 | 
					    protected function usingThemeFolder(callable $callback)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Create a folder and configure a theme
 | 
					        // Create a folder and configure a theme
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function test_image_upload()
 | 
					    public function test_image_upload()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $admin = $this->getAdmin();
 | 
					        $admin = $this->getAdmin();
 | 
				
			||||||
        $this->actingAs($admin);
 | 
					        $this->actingAs($admin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,7 +38,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function test_image_display_thumbnail_generation_does_not_increase_image_size()
 | 
					    public function test_image_display_thumbnail_generation_does_not_increase_image_size()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $admin = $this->getAdmin();
 | 
					        $admin = $this->getAdmin();
 | 
				
			||||||
        $this->actingAs($admin);
 | 
					        $this->actingAs($admin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -108,7 +108,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function test_image_usage()
 | 
					    public function test_image_usage()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $editor = $this->getEditor();
 | 
					        $editor = $this->getEditor();
 | 
				
			||||||
        $this->actingAs($editor);
 | 
					        $this->actingAs($editor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -128,7 +128,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function test_php_files_cannot_be_uploaded()
 | 
					    public function test_php_files_cannot_be_uploaded()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $admin = $this->getAdmin();
 | 
					        $admin = $this->getAdmin();
 | 
				
			||||||
        $this->actingAs($admin);
 | 
					        $this->actingAs($admin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -150,7 +150,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function test_php_like_files_cannot_be_uploaded()
 | 
					    public function test_php_like_files_cannot_be_uploaded()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $admin = $this->getAdmin();
 | 
					        $admin = $this->getAdmin();
 | 
				
			||||||
        $this->actingAs($admin);
 | 
					        $this->actingAs($admin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -202,7 +202,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
        ];
 | 
					        ];
 | 
				
			||||||
        foreach ($badNames as $name) {
 | 
					        foreach ($badNames as $name) {
 | 
				
			||||||
            $galleryFile = $this->getTestImage($name);
 | 
					            $galleryFile = $this->getTestImage($name);
 | 
				
			||||||
            $page = Page::first();
 | 
					            $page = Page::query()->first();
 | 
				
			||||||
            $badPath = $this->getTestImagePath('gallery', $name);
 | 
					            $badPath = $this->getTestImagePath('gallery', $name);
 | 
				
			||||||
            $this->deleteImage($badPath);
 | 
					            $this->deleteImage($badPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -227,7 +227,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
        config()->set('filesystems.images', 'local_secure');
 | 
					        config()->set('filesystems.images', 'local_secure');
 | 
				
			||||||
        $this->asEditor();
 | 
					        $this->asEditor();
 | 
				
			||||||
        $galleryFile = $this->getTestImage('my-secure-test-upload.png');
 | 
					        $galleryFile = $this->getTestImage('my-secure-test-upload.png');
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $expectedPath = storage_path('uploads/images/gallery/' . Date('Y-m') . '/my-secure-test-upload.png');
 | 
					        $expectedPath = storage_path('uploads/images/gallery/' . Date('Y-m') . '/my-secure-test-upload.png');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
 | 
					        $upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
 | 
				
			||||||
@@ -245,7 +245,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
        config()->set('filesystems.images', 'local_secure');
 | 
					        config()->set('filesystems.images', 'local_secure');
 | 
				
			||||||
        $this->asEditor();
 | 
					        $this->asEditor();
 | 
				
			||||||
        $galleryFile = $this->getTestImage('my-secure-test-upload.png');
 | 
					        $galleryFile = $this->getTestImage('my-secure-test-upload.png');
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $expectedPath = storage_path('uploads/images/gallery/' . Date('Y-m') . '/my-secure-test-upload.png');
 | 
					        $expectedPath = storage_path('uploads/images/gallery/' . Date('Y-m') . '/my-secure-test-upload.png');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
 | 
					        $upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
 | 
				
			||||||
@@ -282,7 +282,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function test_image_delete()
 | 
					    public function test_image_delete()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $this->asAdmin();
 | 
					        $this->asAdmin();
 | 
				
			||||||
        $imageName = 'first-image.png';
 | 
					        $imageName = 'first-image.png';
 | 
				
			||||||
        $relPath = $this->getTestImagePath('gallery', $imageName);
 | 
					        $relPath = $this->getTestImagePath('gallery', $imageName);
 | 
				
			||||||
@@ -304,7 +304,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function test_image_delete_does_not_delete_similar_images()
 | 
					    public function test_image_delete_does_not_delete_similar_images()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $this->asAdmin();
 | 
					        $this->asAdmin();
 | 
				
			||||||
        $imageName = 'first-image.png';
 | 
					        $imageName = 'first-image.png';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -383,7 +383,7 @@ class ImageTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function test_deleted_unused_images()
 | 
					    public function test_deleted_unused_images()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $page = Page::first();
 | 
					        $page = Page::query()->first();
 | 
				
			||||||
        $admin = $this->getAdmin();
 | 
					        $admin = $this->getAdmin();
 | 
				
			||||||
        $this->actingAs($admin);
 | 
					        $this->actingAs($admin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user