tapis_job-1.4.1-alpha1/src/Controller/JobInputFileController.php
src/Controller/JobInputFileController.php
<?php
namespace Drupal\tapis_job\Controller;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\file\FileInterface;
use Drupal\jwt\Transcoder\JwtTranscoderInterface;
use Drupal\tapis_job\TapisProvider\TapisJobProviderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Class JobInputFileController.
*
* This class is used to create a controller
* that allows Tapis to download a job's input file.
*
* @package Drupal\tapis_job\Controller
*/
class JobInputFileController extends ControllerBase {
/**
* The Tapis Job provider.
*
* @var \Drupal\tapis_job\TapisProvider\TapisJobProviderInterface
*/
protected TapisJobProviderInterface $tapisJobProvider;
/**
* The JWT transcoder service.
*
* @var \Drupal\jwt\Transcoder\JwtTranscoderInterface
*/
protected JwtTranscoderInterface $jwtTranscoder;
/**
* The configuration factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface|\Drupal\Core\Config\ImmutableConfig
*/
protected ConfigFactoryInterface|ImmutableConfig $config;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* JobOutputController constructor.
*
* @param \Drupal\tapis_job\TapisProvider\TapisJobProviderInterface $tapisJobProvider
* The Tapis Job provider.
* @param \Drupal\jwt\Transcoder\JwtTranscoderInterface $jwt_transcoder
* The JWT transcoder service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The configuration factory.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
*/
public function __construct(TapisJobProviderInterface $tapisJobProvider,
JwtTranscoderInterface $jwt_transcoder,
ConfigFactoryInterface $config_factory,
EntityTypeManagerInterface $entityTypeManager) {
$this->tapisJobProvider = $tapisJobProvider;
$this->jwtTranscoder = $jwt_transcoder;
$this->config = $config_factory->get('tapis_job.config');
$this->entityTypeManager = $entityTypeManager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('tapis_job.tapis_job_provider'),
$container->get('jwt.transcoder'),
$container->get('config.factory'),
$container->get('entity_type.manager')
);
}
/**
* Download a job's input file.
*
* @param \Drupal\file\FileInterface $file
* The file interface.
* @param string $filename
* The file name.
* @param \Symfony\Component\HttpFoundation\Request $request
* The HTTP request.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException|\Drupal\jwt\Transcoder\JwtDecodeException
*/
public function getJobInputFile(FileInterface $file, string $filename, Request $request) {
/*
* Note how we define the $filename parameter as the trailing path parameter
* for this controller (see routing.yml for this method)
* but we don't use it anywhere.
*
* The reason we have the route structured like this is
* because Tapis detects the filename to download to,
* based on the suffix of this download url (instead of reading from
* the Content-Disposition response header).
* So, if we don't have this extra path parameter,
* Tapis will download the file as <file id> instead
* (since that's the second to last trailing part of this url,
* after the filename parameter).
*/
if (!$file) {
return new Response('File not found.', 404);
}
if ($request->getMethod() !== "GET") {
return new Response('', 405);
}
$jwt_token = $request->query->get('token');
// $config = \Drupal::config('tapis_job.config');
$jwt_secret_key_id = $this->config->get("webhook_jwt_secret_key_id");
if ($jwt_secret_key_id) {
// A JWT secret key has been configured,
// so use it to verify the JWT token.
if (!$jwt_token) {
return new Response('', 401);
}
// $jwt_secret_key = \Drupal::entityTypeManager()->getStorage('key')->load($jwt_secret_key_id);
/** @var \Drupal\key\KeyInterface $jwt_secret_key */
$jwt_secret_key = $this->entityTypeManager->getStorage('key')->load($jwt_secret_key_id);
if (!$jwt_secret_key) {
return new Response('', 401);
}
// Verify the JWT token.
// $jwt_transcoder = \Drupal::service('jwt.transcoder');.
$this->jwtTranscoder->setKey($jwt_secret_key);
// This will throw an exception if the token is invalid.
$jwt = $this->jwtTranscoder->decode($jwt_token);
// Verify that the subject of the JWT token matches the file id
// note: here we match on file id and NOT job uuid,
// because when generating in the input file
// download urls for the job submission request,
// we have not yet made the job, so we don't have
// a job uuid to match against.
if ((string) $jwt->getClaim('sub') !== (string) $file->id()) {
return new Response('', 401);
}
}
$uri = $file->getFileUri();
$response = new BinaryFileResponse($uri);
\Drupal::logger('tapis_job')->info('Downloaded input file: ' . $file->getFilename() . ' (file id: ' . $file->id() . '), mime type: ' . $file->getMimeType());
// Optional: Set headers to force download.
$response->headers->set('Content-Type', $file->getMimeType());
$response->headers->set('Content-Disposition', 'attachment; filename="' . $file->getFilename() . '";');
return $response;
}
}
