Packages

Signs URLs for CloudFront distributions

Current section

3 Versions

Jump to

Compare versions

10 files changed
+192 additions
-76 deletions
  @@ -1,4 +1,4 @@
1 - # Used by "mix format"
2 1 [
3 - inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
2 + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
3 + plugins: [Styler]
4 4 ]
  @@ -6,11 +6,52 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6 6
7 7 ## [Unreleased]
8 8
9 + ## 1.0.0 - 2025-02-10
10 +
11 + ### ⚠️ Breaking Changes
12 +
13 + - Remove Application module - users must now add `CloudfrontSigner.DistributionRegistry` to their own supervision tree
14 +
15 + #### Example
16 +
17 + ```elixir
18 + # In your application.ex
19 + def start(_type, _args) do
20 + children = [
21 + # ... other children ...
22 + CloudfrontSigner.DistributionRegistry
23 + ]
24 +
25 + opts = [strategy: :one_for_one, name: YourApp.Supervisor]
26 + Supervisor.start_link(children, opts)
27 + end
28 + ```
29 +
30 + ### 🚀 Features
31 +
32 + - Enforce key order in AWS Policy using Jason.OrderedObject
33 + - Add support for Elixir version 1.15
34 + - Add Styler for consistent code formatting
35 +
36 + ### 🚜 Refactor
37 +
38 + - Remove unused Poison dependency
39 + - Fix test expectations in CloudfrontSignerTest
40 + - Remove test for non-existent module
41 + - Replace Timex with DateTime
42 +
43 + ### 📚 Documentation
44 +
45 + - Update README with guidance for installing via hex
46 + - Add directions for adding registry to application supervision tree
47 + - Improve function docs and typespecs
48 + - Improve test documentation and formatting
49 +
9 50 ## 0.2.0 - 2025-01-28
10 51
11 52 ### 🚀 Features
12 53
13 - - Swap Poison for Jason.
54 + - Swap Poison for Jason
14 55
15 56 ### 🐛 Bug Fixes
  @@ -1,44 +1,66 @@
1 1 # CloudfrontSigner
2 2
3 3 Elixir implementation of Cloudfront's url signature algorithm. Supports expiration policies and
4 - runtime configurable distributions. Fork of https://github.com/Frameio/cloudfront-signer
4 + runtime configurable distributions.
5 +
6 + The main benefits that this library provides are:
7 +
8 + - Runtime configurable distributions
9 + - Caching of PEM decodes
5 10
6 11 ## Installation
7 12
8 - The patched package can be installed
9 - by adding `cloudfront_signer` to your list of dependencies in `mix.exs` as a git based dependency:
13 + Add `cloudfront_signer` to your list of dependencies in `mix.exs`:
10 14
11 15 ```elixir
12 16 def deps do
13 17 [
14 - {:cloudfront_signer,
15 - git: "https://github.com/podium/cloudfront-signer.git",
16 - ref: "73b53cf1364d92708f43ca60dc7150a61cfa5191"}
18 + {:cloudfront_signer, "~> 1.0.0"}
17 19 ]
18 20 end
19 21 ```
20 22
21 - Consult the [mix documentation for git based dependencies](https://hexdocs.pm/mix/1.16.0/Mix.Tasks.Deps.html) for valid syntax options.
23 + ## Configuring a Distribution
22 24
23 25 Configure a distribution with:
24 26
25 27 ```elixir
26 28 config :my_app, :my_distribution,
27 29 domain: "https://some.cloudfront.domain",
28 - private_key: {:system, "ENV_VAR"}, # or {:file, "/path/to/key"}
29 - key_pair_id: {:system, "OTHER_ENV_VAR"}
30 + private_key: System.get_env("PRIVATE_KEY"), # or {:file, "/path/to/key"}
31 + key_pair_id: System.get_env("KEY_PAIR_ID")
30 32 ```
31 33
32 - Then simply do:
34 + ## Signing a URL without Caching PEM Decodes
35 +
36 + Caching PEM decodes is a wise choice, but if you don't want to cache them, you can do the following:
33 37
34 38 ```elixir
35 39 CloudfrontSigner.Distribution.from_config(:my_app, :my_distribution)
36 40 |> CloudfrontSigner.sign(path, [arg: "value"], expiry_in_seconds)
37 41 ```
38 42
39 - If you want to cache pem decodes (which is a wise choice), a registry of decoded distributions is available. Simply do:
43 + ## Caching PEM Decodes
44 +
45 + If you want to cache PEM decodes, you can use the distribution registry.
46 + Add `CloudfrontSigner.DistributionRegistry` to your application's supervision tree:
47 +
48 + ```elixir
49 + # In your application.ex
50 + def start(_type, _args) do
51 + children = [
52 + # ... other children ...
53 + CloudfrontSigner.DistributionRegistry
54 + ]
55 +
56 + opts = [strategy: :one_for_one, name: YourApp.Supervisor]
57 + Supervisor.start_link(children, opts)
58 + end
59 + ```
60 +
61 + Then use it like:
40 62
41 63 ```elixir
42 64 CloudfrontSigner.DistributionRegistry.get_distribution(:my_app, :my_distribution)
43 - |> CloudfrontSigner.sign(path, [arg: "value], expiry)
65 + |> CloudfrontSigner.sign(path, [arg: "value"], expiry)
44 66 ```
  @@ -1,12 +1,12 @@
1 1 {<<"links">>,
2 2 [{<<"Changelog">>,
3 3 <<"https://github.com/podium/cloudfront-signer/blob/master/CHANGELOG.md">>},
4 - {<<"Docs">>,<<"https://hexdocs.pm/cloudfront_signer/0.2.0/">>},
4 + {<<"Docs">>,<<"https://hexdocs.pm/cloudfront_signer/1.0.0/">>},
5 5 {<<"GitHub">>,<<"https://github.com/podium/cloudfront-signer">>}]}.
6 6 {<<"name">>,<<"cloudfront_signer">>}.
7 - {<<"version">>,<<"0.2.0">>}.
7 + {<<"version">>,<<"1.0.0">>}.
8 8 {<<"description">>,<<"Signs URLs for CloudFront distributions">>}.
9 - {<<"elixir">>,<<"~> 1.16">>}.
9 + {<<"elixir">>,<<"~> 1.15">>}.
10 10 {<<"app">>,<<"cloudfront_signer">>}.
11 11 {<<"licenses">>,[<<"MIT">>]}.
12 12 {<<"requirements">>,
  @@ -14,17 +14,11 @@
14 14 {<<"app">>,<<"jason">>},
15 15 {<<"optional">>,false},
16 16 {<<"requirement">>,<<"~> 1.4">>},
17 - {<<"repository">>,<<"hexpm">>}],
18 - [{<<"name">>,<<"timex">>},
19 - {<<"app">>,<<"timex">>},
20 - {<<"optional">>,false},
21 - {<<"requirement">>,<<"~> 3.7">>},
22 17 {<<"repository">>,<<"hexpm">>}]]}.
23 18 {<<"files">>,
24 19 [<<"lib">>,<<"lib/cloudfront_signer.ex">>,<<"lib/cloudfront_signer">>,
25 20 <<"lib/cloudfront_signer/policy.ex">>,
26 21 <<"lib/cloudfront_signer/distribution_registry.ex">>,
27 - <<"lib/cloudfront_signer/distribution.ex">>,
28 - <<"lib/cloudfront_signer/application.ex">>,<<".formatter.exs">>,
22 + <<"lib/cloudfront_signer/distribution.ex">>,<<".formatter.exs">>,
29 23 <<"mix.exs">>,<<"README.md">>,<<"LICENSE">>,<<"CHANGELOG.md">>]}.
30 24 {<<"build_tools">>,[<<"mix">>]}.
  @@ -14,16 +14,18 @@ defmodule CloudfrontSigner do
14 14 Signs a url for the given `Distribution.t` struct constructed from the `path` and `query_params` provided. `expiry`
15 15 is in seconds.
16 16 """
17 - @spec sign(Distribution.t(), binary | list | map, list, integer) :: binary
18 - def sign(
19 - %Distribution{domain: domain, private_key: pk, key_pair_id: kpi},
20 - path,
21 - expiry,
22 - query_params \\ []
23 - ) do
24 - expiry = Timex.now() |> Timex.shift(seconds: expiry) |> Timex.to_unix()
25 - base_url = URI.merge(domain, path) |> to_string()
26 - url = url(base_url, query_params)
17 + @spec sign(Distribution.t(), binary() | list() | map(), integer(), list()) :: binary()
18 + def sign(%Distribution{domain: domain, private_key: pk, key_pair_id: kpi}, path, expiry, query_params \\ []) do
19 + expiry =
20 + DateTime.utc_now()
21 + |> DateTime.add(expiry, :second)
22 + |> DateTime.to_unix()
23 +
24 + url =
25 + domain
26 + |> URI.merge(path)
27 + |> to_string()
28 + |> url(query_params)
27 29
28 30 {signature, encoded_policy} =
29 31 Policy.generate_signature_and_policy(%Policy{resource: url, expiry: expiry}, pk)
Loading more files…