Skip to content

[draft] DGPF-extensible rendering of files based on public URL#244

Draft
abought wants to merge 13 commits into
globus:mainfrom
abought:feature/snn-url-renderer
Draft

[draft] DGPF-extensible rendering of files based on public URL#244
abought wants to merge 13 commits into
globus:mainfrom
abought:feature/snn-url-renderer

Conversation

@abought

@abought abought commented Feb 19, 2025

Copy link
Copy Markdown
Contributor

🦑 Not a candidate for merge. Here to communicate work in progress, currently targeting my own needs. Very much a work in progress, so there are some big quirks!

Purpose

Demonstrate a possible mechanism for rendering files within DGPF based on a public URL, with a pathway towards showing private files later.

Current state

NOT a candidate for merge, but exposing to show one option if there is broader interest. UUID of test collection / search index available on request.

Feature set

To see if the rendering is generic enough to be extensible, I've written crude renderers for image, text, source code highlighting, interactive data tables, PDF, and will write a custom page with interactive widgets around PDB files next.

Screenshot 2025-02-19 at 12 59 19 AM

Arch plan

This PR encompasses:

  • A new route /<index>/render?url=&collection_uuid=&path= that can render either a public URL, or (eventually) auto-fetch user-approved bearer tokens for private file access (internally translating collection_uuid+ path into an https url for that collection)
    • All rendering can be done inside a sandboxed iframe, so badly implemented science widgets don't stomp on the DOM for everyone else
    • Multiple iframes can be rendered to handle multiple preview files, in custom template logic
  • Provides a set of extensible renderers that decides what to show via content-type (may expose other choices later). A custom DGPF can add their own renderer with minor additional code.

The path to a more generic featureset would be:

  • Allow each index to have a new configuration option specifying a) where to find the preview file, b) what to do with it, and c) a mandatory whitelist of collections that are allowed to work with authentication and send credentials to frontend
  • A new django admin command that would check the collections whitelisted and compare against the DGPF login scopes, to help guide devs around pitfalls of improperly configured apps
  • (someday) incremental reauth for collection access if we aren't able to predict the needed collections (if such a need arises)

Open questions

The main open question is around token leakage. To avoid proxying file requests through the web app, we need to pass a token to the frontend.

The <collection>/https token may have substantially more power than just viewing files. And because all scopes on a resource server are attached to a single token, if a custom DGPF carries extra scopes, then a leaked token could be exfiltrated and used to do a lot of other stuff on behalf of a user. That would be.... bad.

Open for discussion/ brainstorming here!

Using the view

Since my test case is a private repo, examples are produced below. These are very crude and will evolve as PR grows.

          {% url 'render_asset' globus_portal_framework.index as asset_url %}
          <iframe
            sandbox="allow-same-origin allow-scripts allow-downloads"
            src="{{ asset_url }}?url={{ item.preview_url | urlencode }}"
            width="500px"
            height="300px"></iframe>

Extensibility mechanism

Below is a real world example of an index-specific override, showing that the extensibility mechanism allows defining project-specific custom renderers.

{% extends 'globus-portal-framework/v3/detail-render-asset.html' %}
{% load static %}

{% block headextras %}
  {{ block.super }}
  {# Add PDB rendering support #}
  <script src="https://3Dmol.org/build/3Dmol-min.js"></script>
  <script type="module">
    import RENDERERS, {fetchText, sizeToPage} from "{% static 'js/render-asset/renderers.js' %}";
    function renderProtein(target, urlOptions, renderOptions) {
      return fetchText(urlOptions)
        .then((text) => {
          sizeToPage(target, target);  // Must size first in order to zoom correctly
          const viewer = $3Dmol.createViewer( target, {} );
          viewer.addModel( text, "cif" );
      });
    }
    // When main.js sees a PDB file, it will know to use this extra new function
    RENDERERS.add('chemical/x-cif', renderProtein);
  </script>
{% endblock %}

TODO

The following items are based on my own needs, and represent a potential way forward if this ever seemed useful for others.

Features

  • Possibly add CSP headers to DGPF for the render route to complement sandboxing, similar to Joe's SSP feature. Allows more confident rendering of user-specified data due to XSS potential of this feature.
  • There's some interest in, eg, plotly renderers for graphs.

Technology

[ ] Review current state around native ESM in browser. If the team is concerned about compat, parcel/vite/etc may be useful- but this comes with tradeoffs.

  • Consider configuration syntax for app around collections whitelist. Missing use case: "this asset is public and no token needed, but I still want to translate collection ID into URL".
  • Consider exposing app-owned HTTPS tokens rather than user-owned tokens. This would ensure that even if the token leaks, it is read-only. This only makes sense if used with the "restrict login to specific globus group" setting.
  • Unit tests. Since this has so much interactive UI, give thought to how we'd keep this maintainable over time.
  • In unauthenticated mode, embed object tag members using src url instead of blob. For native video elements, this might enable streaming use case
  • Switch to Bootstrap responsive embed helper classes and away from fixed JS resizing

@abought
abought force-pushed the feature/snn-url-renderer branch from 80e30eb to 23cd9d9 Compare February 25, 2025 00:14
Comment thread globus_portal_framework/views/base.py Outdated
Comment thread globus_portal_framework/views/base.py Outdated
**page_options,
"render_options": render_options,
}
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this view going to be used in practice? Most "previewed" images and such will be done across multiple pages. This could potentially be done in an iFrame, but I'm unsure.

A better way might be creating an API method instead, that returns the required info as needed. An alternative to this was already implemented here, which allows asynchronously fetching a token using javascript for later use by the page (loading images and such)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually planning to refactor to iframe srcdoc and simplifying to api token for what is fetched; refactor planned next week.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be available this week to workshop some ideas for the django <-> JS interface on a file renderer? Some changes pushed based on tweaks. Best days this week are Tues / Wed.

The current interface between backend and frontend is a bit baroque- it exposes a lot of power, and it could be worse... but it also imposes some mental overhead on the developer. Honestly, it's easier to discuss synchronously (with live demo) to start.

At its heart, the render logic consists of a JS function, doRender, that is passed certain variables from a template. The surrounding template partial fills in the boilerplate, but a page with multiple files could just as easily call that function however it wants.

When it's time to use that function, we need to take advantage of any view function, and ideally without having to rewrite the view function. To inject the functionality, this uses three pieces:

  1. A template tag that turns a given search result into the dict we need (including translating the collection ID into a URL)
  2. A user-extensible template which allows adding any custom render logic (like a PDB viewer)
  3. A backend token endpoint that checks against a whitelist of collection IDs. (I have some questions about DGPF functionality for how to improve this)

I had originally used a URL to abstract this away, which was great for injecting a bundle of functionality into a template; this approach was replaced with the django partial for reasons we could discuss live.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting aside some time to talk about this would be good. This is generally a pretty hard problem to "automatically" with with little user/developer interaction. My ideal list of contradictory items would be:

  • User can configure items (like images, but not exclusively) to be rendered from search data
  • This functionality is not tied to a single view
  • This feature is flexible enough to be used on non-search-related views (User hardcodes items to be rendered)

We do need backend processing for some items like:

  • Fetching https access tokens
  • Determining collection ids from https links (This is actually difficult and maybe unsupported) or creating https links from collection ids (Requires transfer tokens, which we probably don't want to expose).

We could do the backend processing in templatetags or separate API views. templatetags are less ideal if it incudes network calls to Globus Transfer. I think it would be most ideal if we could specify Globus Collection items to be rendered using Django templates.

I'll ping you on Slack to talk more!

abought added 7 commits March 3, 2025 15:58
- get-token as separate endpoint
- whitelist of collections that can be used with preview feature
- cached authentication helper
- Move logic into renderers.js and remove main.js
Although it made it much simpler to use this feature, allowing arbitrary URLs exposed the site to url-specified external content. The more baroque template helper system locks it so that users can only render assets described in the search index.
Allows user to select a facet by clicking either the word or the associated label. (a nice bit of convenience)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants