Current section
3 Versions
Jump to
Current section
3 Versions
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…