Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New registration API for web extensions #6292

Merged
merged 8 commits into from
May 22, 2024
Merged

New registration API for web extensions #6292

merged 8 commits into from
May 22, 2024

Conversation

mperham
Copy link
Collaborator

@mperham mperham commented May 14, 2024

In order to lock down JS and CSS for better security, we need to provide a first-class API for web extensions so they can opt into better asset security while maintaining compatibility with existing versions.

Please see the content under examples/webui-ext and specifically the new register API:

# DEPRECATED --- Old way to register
# Sidekiq::Web.register(SidekiqExt::RedisInfo::Web)
# Sidekiq::Web.tabs["Redis"] = "redis_info"

# NEW API IN 7.3.0
# This new way allows you to serve your own static assets
# and provide localizations for any strings in your extension.

Sidekiq::Web.register(SidekiqExt::RedisInfo::Web,
  name: "redis_info",
  tab: ["Redis"],              # The name on your Tab(s)
  index: ["redis_info"],       # The path to the root page(s) of your extension within the Web UI, usually "/sidekiq/" + index
  root_dir: SidekiqExt::RedisInfo::Web::ROOT, # root directory for all web ui content
  asset_paths: ["css", "js"],  # Paths within {root}/assets/{name} to serve static assets
  cache_for: 86400            # Enable http caching for one day of static assets
) do |sidekiq_web|
  # you can add your own middleware or additional settings here
end

The old way still works but will be removed in 8.0. Note also the new tag helpers script_tag and style_tag which handle the "nonce-sense" (ha ha) for you.

@markets
Copy link

markets commented May 14, 2024

Like the new API @mperham 👍

Just a small suggestion. The middlewares object seems very flexible, but a bit complex maybe 🤔? What about some more high level flag to easily configure the assets paths, something like:

Sidekiq::Web.register(SidekiqExt::RedisInfo::Web,
  tab: "Redis",
  index: "redis_info",
  asset_paths: ["/redis_info/css", "/redis_info/js"]

And rely on some sensible defaults for the rest.

Changes.md Outdated
----------

- Allow `Sidekiq::Limiter.redis` to use Redis Cluster [#6288]
- Adjust CSP to disallow inline styling and scripts within the Web UI [#6270]
Copy link
Contributor

@Earlopain Earlopain May 15, 2024

Choose a reason for hiding this comment

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

Inline styling got reverted and inline scripts are already disallowed (#6074). This will be more accurate:

Adjust CSP to disallow remote scripts within the Web UI.
... 3rd party extensions that load scripts from something like a CDN will need to adjust their web assets accordingly. Inline and external styles will be disallowed in sidekiq 8.0.

Also, perhaps something like:

# Before
<script type="text/javascript" src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
# After
<script type="text/javascript" src="https://code.jquery.com/jquery-3.7.1.min.js" <%= defined?(csp_nonce) ? "nonce=\"#{csp_nonce}\"" : "" %>></script>

This will work in both 7.2 and 7.3 so extensions wouldn't have to worry about that.

Mention that the same will be necessary for css for 8.0 so that people already prepare.

@mperham
Copy link
Collaborator Author

mperham commented May 15, 2024

Just a small suggestion. The middlewares object seems very flexible, but a bit complex maybe 🤔? What about some more high level flag to easily configure the assets paths, something like:

Sidekiq::Web.register(SidekiqExt::RedisInfo::Web,
  tab: "Redis",
  index: "redis_info",
  asset_paths: ["/redis_info/css", "/redis_info/js"]

The issue is that I have to make a lot of assumptions to remove most of that boilerplate code. I want to include some caching of assets but can I assume that? Is a one day expiry ok? How do I know where the assets are located on disk (the root parameter)? I think I can do something like this:

Sidekiq::Web.register(SidekiqExt::RedisInfo::Web,
  tab: "Redis",
  index: "redis_info",
  asset_urls: ["/redis_info/css", "/redis_info/js"],
  root: SidekiqExt::RedisInfo::Web::ROOT,
  cache_for: 86400)

If we assume that ROOT/assets, ROOT/locales, ROOT/views is a directory convention we can rely on.

@markets
Copy link

markets commented May 15, 2024

Much simpler in my opinion 👌

In general I feel that: Conventions > Configuration.

I understand your concerns about some defaults, like caching, but since this panel is not critical or public facing, probably any sensible default is fine (and anyway, we can keep the flags so anyone is able to override them if needed).

@mperham
Copy link
Collaborator Author

mperham commented May 16, 2024

Here's the latest API from the (great!) feedback so far:

Sidekiq::Web.register(SidekiqExt::RedisInfo::Web,
  name: "redis_info",
  tab: ["Redis"],              # The name on your Tab(s)
  index: ["redis_info"],       # The path to the root page(s) of your extension within the Web UI, usually "/sidekiq/" + index
  root_dir: SidekiqExt::RedisInfo::Web::ROOT, # root directory for all web ui content
  asset_paths: ["css", "js"],  # Paths within {root}/assets/{name} to serve static assets
  cache_for: 86400            # Enable http caching for one day of static assets
) do |sidekiq_web|
  # you can add your own middleware or additional settings here
end

Ideally extensions have a directory layout like so:

web/ <-- root
  locales/
  views/
  assets/
    {name}/
      js/
      css/
      fonts/
      img/

Assets must have {name} in the path in order to prevent asset collisions between different extensions. An example URL for an asset: http://localhost:3000/sidekiq/redis_info/js/script.js Without redis_info, that js/script.js could belong to any extension.

Tabs/Index can now be Arrays because I noticed sidekiq-unique-jobs adds multiple Tabs. Locales are automatically added if Sidekiq sees a {root}/locales directory.

@markets
Copy link

markets commented May 16, 2024

Nice job @mperham 👍 Really like this version!

@mperham
Copy link
Collaborator Author

mperham commented May 17, 2024

Ok, I'm happy with this PR as is. Any further feedback? cc @mhenrixon

@mhenrixon
Copy link
Contributor

This looks great to me! Solves more than a few of the issues I've had reported 👍

@mperham mperham merged commit 778de29 into main May 22, 2024
16 checks passed
@mperham mperham deleted the webui_extension branch May 22, 2024 16:47
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.

None yet

4 participants