|
|
|
|
@ -23,7 +23,6 @@ use OCP\AppFramework\Http;
|
|
|
|
|
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
|
|
|
|
use OCP\AppFramework\Http\Attribute\OpenAPI;
|
|
|
|
|
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
|
|
|
|
|
use OCP\AppFramework\Http\Attribute\PublicPage;
|
|
|
|
|
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
|
|
|
|
use OCP\AppFramework\Http\FileDisplayResponse;
|
|
|
|
|
use OCP\AppFramework\Http\JSONResponse;
|
|
|
|
|
@ -45,7 +44,9 @@ use OCP\IL10N;
|
|
|
|
|
use OCP\INavigationManager;
|
|
|
|
|
use OCP\IRequest;
|
|
|
|
|
use OCP\IURLGenerator;
|
|
|
|
|
use OCP\IUserSession;
|
|
|
|
|
use OCP\L10N\IFactory;
|
|
|
|
|
use OCP\Security\RateLimiting\ILimiter;
|
|
|
|
|
use OCP\Server;
|
|
|
|
|
use OCP\Util;
|
|
|
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
|
@ -129,9 +130,8 @@ class AppSettingsController extends Controller {
|
|
|
|
|
* @param string $image
|
|
|
|
|
* @throws \Exception
|
|
|
|
|
*/
|
|
|
|
|
#[PublicPage]
|
|
|
|
|
#[NoCSRFRequired]
|
|
|
|
|
public function getAppDiscoverMedia(string $fileName): Response {
|
|
|
|
|
public function getAppDiscoverMedia(string $fileName, ILimiter $limiter, IUserSession $session): Response {
|
|
|
|
|
$getEtag = $this->discoverFetcher->getETag() ?? date('Y-m');
|
|
|
|
|
$etag = trim($getEtag, '"');
|
|
|
|
|
|
|
|
|
|
@ -161,6 +161,26 @@ class AppSettingsController extends Controller {
|
|
|
|
|
$file = reset($file);
|
|
|
|
|
// If not found request from Web
|
|
|
|
|
if ($file === false) {
|
|
|
|
|
$user = $session->getUser();
|
|
|
|
|
// this route is not public thus we can assume a user is logged-in
|
|
|
|
|
assert($user !== null);
|
|
|
|
|
// Register a user request to throttle fetching external data
|
|
|
|
|
// this will prevent using the server for DoS of other systems.
|
|
|
|
|
$limiter->registerUserRequest(
|
|
|
|
|
'settings-discover-media',
|
|
|
|
|
// allow up to 24 media requests per hour
|
|
|
|
|
// this should be a sane default when a completely new section is loaded
|
|
|
|
|
// keep in mind browsers request all files from a source-set
|
|
|
|
|
24,
|
|
|
|
|
60 * 60,
|
|
|
|
|
$user,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!$this->checkCanDownloadMedia($fileName)) {
|
|
|
|
|
$this->logger->warning('Tried to load media files for app discover section from untrusted source');
|
|
|
|
|
return new NotFoundResponse(Http::STATUS_BAD_REQUEST);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$client = $this->clientService->newClient();
|
|
|
|
|
$fileResponse = $client->get($fileName);
|
|
|
|
|
@ -182,6 +202,31 @@ class AppSettingsController extends Controller {
|
|
|
|
|
return $response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function checkCanDownloadMedia(string $filename): bool {
|
|
|
|
|
$urlInfo = parse_url($filename);
|
|
|
|
|
if (!isset($urlInfo['host']) || !isset($urlInfo['path'])) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Always allowed hosts
|
|
|
|
|
if ($urlInfo['host'] === 'nextcloud.com') {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hosts that need further verification
|
|
|
|
|
// Github is only allowed if from our organization
|
|
|
|
|
$ALLOWED_HOSTS = ['github.com', 'raw.githubusercontent.com'];
|
|
|
|
|
if (!in_array($urlInfo['host'], $ALLOWED_HOSTS)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (str_starts_with($urlInfo['path'], '/nextcloud/') || str_starts_with($urlInfo['path'], '/nextcloud-gmbh/')) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove orphaned folders from the image cache that do not match the current etag
|
|
|
|
|
* @param ISimpleFolder $folder The folder to clear
|
|
|
|
|
|