Score:1

Key based access control for private files

jp flag

I'm building a site that provides paid video content, for embedding in customer's own sites. I'm successfully controlling access to the nodes that contain the video content based on purchasable user roles.

Now I need to provide users with an embed code which ideally would only work from their domain. For now I'd be happy just to be able to limit it to specific users.

I have installed the key_auth and restui modules, and I have enabled the 'file' REST resource. I can now access information *about* the file as an anonymous user with the paying subscriber's api key in the query. However I can't access the actual file.

The info I get back is below.

{"fid":[{"value":3}],"uuid":[{"value":"5655ecda-b421-4ab8-aa92-d43c6c0fbfdf"}],"langcode":[{"value":"en"}],"uid":[{"target_id":1,"target_type":"user","target_uuid":"58d862eb-86e9-4ec6-bb33-89bda5c1767a","url":"\/user\/1"}],"filename":[{"value":"file_example_MP4_480_1_5MG.mp4"}],"uri":[{"value":"private:\/\/2022-04\/file_example_MP4_480_1_5MG.mp4","url":"\/system\/files\/2022-04\/file_example_MP4_480_1_5MG.mp4"}],"filemime":[{"value":"video\/mp4"}],"filesize":[{"value":1570024}],"status":[{"value":true}],"created":[{"value":"2022-04-16T15:52:39+00:00","format":"Y-m-d\\TH:i:sP"}],"changed":[{"value":"2022-04-16T15:52:43+00:00","format":"Y-m-d\\TH:i:sP"}]}

How can I get access to the actual video in this way?

apaderno avatar
us flag
I take that by *get access to the actual video* you mean getting the video URL. Since you are using a REST API, I also take the request doesn't come from the Drupal site. The video relative path is given in `uri.url`. To that, you need to add the site URL. For example, if that is `http://example.com/drupal`, that video URL would be `http://example.com/drupal/system/files/2022-04/file_example_MP4_480_1_5MG.mp4`. Since the code is making a REST request, it knows the URL to which the request is sent, which also means it can get the missing part in the video path to make it a complete URL.
apaderno avatar
us flag
Or are you asking *On the Drupal side, how do I alter the values returned from the REST API to include the absolute video URL?*
apaderno avatar
us flag
Alternatively, *get access to the actual video* just means that accessing the video using its URL from outside Drupal returns a 403 error and you want to know how to avoid that. Since the video is saved as private file, on Drupal side the access to those files is controlled by [`hook_file_download()`](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21File%21file.api.php/function/hook_file_download/9.3.x). How an implementation of that hook would understand the file is requested by a *client* that uses REST API to access the information is the "real" question, in this case.
Alfred Armstrong avatar
cn flag
If the video is going to be embedded on the client site, anyone viewing the video must necessarily have access. You can probably prevent it from being embedded on unauthorised sites, since most browsers will respect content security policy headers, but if the video is being served as a file you can't prevent it being copied and then served on other platforms.
griz avatar
jp flag
@AlfredArmstrong Yes, having gone to the lengths described in my answer this became apparent. For this reason I'm now looking at using a video hosting platform that provides protection against this, but its been an interesting journey.
griz avatar
jp flag
@apaderno Thanks for your efforts. What I was trying to do is create a rest endpoint that simply returns the file. That would either result in a download if the GET request is made through a browser, or in the file being displayed in a page if used in a src attribute.
Score:0
jp flag

I had to write a custom module with the following structure: video_resource.info.yml src/Plugin/Deriver/VideoDeriver.php src/Plugin/rest/resource/VideoResource.php

The rest resource and entity deriver are cut-down copies of the EntityResource and EntityDeriver:

\Drupal\rest\Plugin\rest\resource\EntityResource \Drupal\rest\Plugin\Deriver\EntityDeriver

These classes together create a rest resource for every entity type. I set mine to only create a rest resource for the 'File' entity type, with the following code in its get() function:

$filepath = $entity->getFileUri(); $response = new BinaryFileResponse($filepath); return $response;
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.