diff --git a/.gitignore b/.gitignore index 0f2aa7d8..202202ee 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ secrets.tar scratchpad.txt .DS_Store .iex.exs +*.swp diff --git a/.travis.yml b/.travis.yml index fce6283b..7fa6ac2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,3 @@ before_install: - tar xvf secrets.tar script: - MIX_ENV=test mix do deps.get, compile, coveralls.travis -env: - global: - - secure: kd6KF3eMgtaWIQbd/MG8m8L2cR3fqxmgRHWng4fqxRwPH45hP4IVLf2cQctGA9mm6nOV5wrNtys1fQzYPuXAKA/LAYJ/pc66ISdEjKjNpqxIGRCCJsB6lZft5XPgpAJTClwKGpFvEd7cLtz2ir4PBN0JjDyWtMTKHXarKzn7cOQNpRypwc5wWU1dmuqSsNNjr2hk3HUAwSNqB8W960Zqd39lqo0m+/tlq3L8LghcFlibn8XFNlzAmERpUpERkJZPbRplgx+ofigATGaC9Tzuc04WWLUthsPyBJkli2tYs/8Yd1tCjAYz90Tde0E1/Mzj6b4/LW4AGXrb4I5pKZrkkDw0gjqJ1Buu4U2d/TGZc+wU13BD5icpjq7zx8ZZ08N3XmQ6Y2ydd5p9MwCtUx5Dg2d9ybvdeCI1kj4HVcYAxqoBmf12od3fRZ+IwzbpUIvFn8R05wlvhA8nSLGloUceag3qUHJLD/tJOj9GRgR8ubw6hAQz7w7uWJhq3XSvHvdtYw2tfc9pGpjloA2gOhaStHrjJGFpzMFPITCEcQF1B7KraY/FPxMyvQL70l9RhQZ11F+56uwZ9oZFEQeyr5Tr6mPISyp1enz50HjwRrVy4pw5hOaTOCxA1dxrstWPhdKHOyYNtZDMFopn8yAIGzPE3z6UQ4bABqIlz8tpEEy13Eg= - - secure: f7LJkl/HyS/YghhADhVSNZjOsYkzuWXL9kqYAqjTxzB4g/2HPxkyjGjtlUHv82MvjvoIs7icDNjIveVf0ZP/is5t5T88HXfobYIW1H4hexXbN94oLJTaehdlkv0Fdi9Rfm7sBBH+tyd9HwS25R9MMSndRkxcnr78huFP8iWgRFFF4dDHyh8Dozzo+tHbrPPJtt8iJPz1Fs8U3nMQOdwHj8cA3fZXg65S7IzeavGAnkxtUDV22FgJd0PLD2i2RHAXAxjpmjLY3GIZDquSAO9DWJGElNRphg91nIT726vylm6IZ0JQ0H13hXbVfD4akYYb9ipL8CS/FzZexArtd1cg/nJKSz4G6AJyfByw0KqJwUgaPhvhlCeG8RDse4CH9dH7d8ODbUpxkcvh3Z31EUECY/2BC7Ma4ieLC4VAMBBgc21XxMPklt5z+wW8v2mblVudTSIRnXLL3QiiM2SHV0PLHyH7TPGiKU92J5HrhT3cKkrUMwcePLb6GUBKg6tSiF52DEiiGXWDWbj3rMEnwRbvAa5QkHNhVLwre1tsOt8nCX6cK3rY5fygR3W7lfzMQGMs/XzMdMaqFamJ0YRqUMh2msL0SdzltqFsXlS80JWS8U3zM1IlS448/qmhHzDjyV5mW/bd5iC7uDPQ/5rJfSaicfS1pNNp83Ufg7byNVaIbL4= diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cd0128d..cce64c3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,27 @@ # Changelog +## v1.5.0 +* Bumped minimum Elixir version to 1.6 +* Raise `Pigeon.ConfigError` when booting invalid config structs. + See below for validated keys and error types. +* `APNS.JWTConfig` now validates key p8 content before connecting. +* Relaxed `gen_stage` dependency to allow `~> 1.0` + +Validated config keys: +- `ADM.Config` - `:client_id`, `:client_secret` +- `APNS.Config` - `:cert`, `:key` +- `APNS.JWTConfig` - `:team_id`, `:key`, `:key_identifier` +- `FCM.Config` - `:key` + +Possible error values: + - `{:error, {:invalid, value}}` + - `{:error, {:nofile, value}}` + ## v1.4.0 * `apns-push-type` header support for iOS 13. An additional `:push_type` key has been added to the `APNS.Notification` struct. ## v1.3.2 - * Document workers configuration for run-time configuration of push workers. * Modify run-time configuration of push workers so that multiple (or no) workers may be returned by the startup configuration. diff --git a/README.md b/README.md index 5fc71911..83fde76d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Add pigeon and kadabra as `mix.exs` dependencies: ```elixir def deps do [ - {:pigeon, "~> 1.4.0"}, + {:pigeon, "~> 1.5.0"}, {:kadabra, "~> 0.4.4"} ] end diff --git a/lib/pigeon/adm/config.ex b/lib/pigeon/adm/config.ex index 36586446..aeb13147 100644 --- a/lib/pigeon/adm/config.ex +++ b/lib/pigeon/adm/config.ex @@ -55,4 +55,25 @@ defmodule Pigeon.ADM.Config do end defp valid_item?(item), do: is_binary(item) and String.length(item) > 0 + + @spec validate!(any) :: :ok + def validate!(config) do + if valid?(config) do + :ok + else + raise Pigeon.ConfigError, + reason: "attempted to start without valid client id and secret", + config: redact(config) + end + end + + defp redact(config) do + [:client_id, :client_secret] + |> Enum.reduce(config, fn k, acc -> + case Map.get(acc, k) do + nil -> acc + _ -> Map.put(acc, k, "[FILTERED]") + end + end) + end end diff --git a/lib/pigeon/adm/worker.ex b/lib/pigeon/adm/worker.ex index 66aa21d5..82023c98 100644 --- a/lib/pigeon/adm/worker.ex +++ b/lib/pigeon/adm/worker.ex @@ -25,6 +25,7 @@ defmodule Pigeon.ADM.Worker do def init({:ok, config}), do: initialize_worker(config) def initialize_worker(config) do + Pigeon.ADM.Config.validate!(config) {:ok, %{ config: config, diff --git a/lib/pigeon/apns/config.ex b/lib/pigeon/apns/config.ex index 96c50061..4d4719b0 100644 --- a/lib/pigeon/apns/config.ex +++ b/lib/pigeon/apns/config.ex @@ -116,17 +116,37 @@ defmodule Pigeon.APNS.Config do %__MODULE__{ name: opts[:name], reconnect: Keyword.get(opts, :reconnect, false), - cert: ConfigParser.cert(opts[:cert]), + cert: cert(opts[:cert]), certfile: ConfigParser.file_path(opts[:cert]), - key: ConfigParser.key(opts[:key]), + key: key(opts[:key]), keyfile: ConfigParser.file_path(opts[:key]), uri: Keyword.get(opts, :uri, ConfigParser.uri_for_mode(opts[:mode])), port: Keyword.get(opts, :port, 443), ping_period: Keyword.get(opts, :ping_period, 600_000) } + |> ConfigParser.strip_errors(:cert, :certfile) + |> ConfigParser.strip_errors(:key, :keyfile) end def new(name) when is_atom(name), do: ConfigParser.parse(name) + + defp cert(bin) when is_binary(bin) do + case :public_key.pem_decode(bin) do + [{:Certificate, cert, _}] -> cert + _ -> {:error, {:invalid, bin}} + end + end + + defp cert(other), do: {:error, {:invalid, other}} + + defp key(bin) when is_binary(bin) do + case :public_key.pem_decode(bin) do + [{:RSAPrivateKey, key, _}] -> {:RSAPrivateKey, key} + _ -> {:error, {:invalid, bin}} + end + end + + defp key(other), do: {:error, {:invalid, other}} end defimpl Pigeon.Configurable, for: Pigeon.APNS.Config do @@ -166,12 +186,32 @@ defimpl Pigeon.Configurable, for: Pigeon.APNS.Config do defdelegate close(config), to: Shared - def connect_socket_options(%{cert: nil, certfile: nil}) do - {:error, :invalid_config} + def validate!(config) do + case config do + %{cert: {:error, _}, certfile: {:error, _}} -> + raise Pigeon.ConfigError, + reason: "attempted to start without valid certificate", + config: redact(config) + + %{key: {:error, _}, keyfile: {:error, _}} -> + raise Pigeon.ConfigError, + reason: "attempted to start without valid key", + config: redact(config) + + _ -> + :ok + end end - def connect_socket_options(%{key: nil, keyfile: nil}) do - {:error, :invalid_config} + defp redact(config) do + [:cert, :key] + |> Enum.reduce(config, fn key, acc -> + case Map.get(acc, key) do + bin when is_binary(bin) -> Map.put(acc, key, "[FILTERED]") + {:RSAPrivateKey, _bin} -> Map.put(acc, key, "[FILTERED]") + _ -> acc + end + end) end def connect_socket_options(config) do diff --git a/lib/pigeon/apns/config_parser.ex b/lib/pigeon/apns/config_parser.ex index 434892ca..50d8a3ff 100644 --- a/lib/pigeon/apns/config_parser.ex +++ b/lib/pigeon/apns/config_parser.ex @@ -24,7 +24,7 @@ defmodule Pigeon.APNS.ConfigParser do @spec parse(atom | config_opts) :: config | {:error, :invalid_config} def parse(opts) when is_list(opts) do case config_type(Enum.into(opts, %{})) do - :error -> raise "invalid apns configuration #{inspect(opts)}" + :error -> raise Pigeon.ConfigError, reason: "configuration is invalid", config: opts type -> type.new(opts) end end @@ -42,47 +42,32 @@ defmodule Pigeon.APNS.ConfigParser do defp config_type(_else), do: :error @doc false - def file_path(nil), do: nil - def file_path(path) when is_binary(path) do - path - |> Path.expand() - |> validate_file_path!() + if :filelib.is_file(path) do + Path.expand(path) + else + {:error, {:nofile, path}} + end end def file_path({app_name, path}) when is_atom(app_name) do path |> Path.expand(:code.priv_dir(app_name)) - |> validate_file_path!() + |> file_path() end - @doc false - def cert({_app_name, _path}), do: nil - def cert(nil), do: nil - - def cert(bin) do - case :public_key.pem_decode(bin) do - [{:Certificate, cert, _}] -> cert - _ -> nil - end - end + def file_path(other), do: {:error, {:nofile, other}} @doc false - def key({_app_name, _path}), do: nil - def key(nil), do: nil - - def key(bin) do - case :public_key.pem_decode(bin) do - [{:RSAPrivateKey, key, _}] -> {:RSAPrivateKey, key} - _ -> nil + def strip_errors(config, key1, key2) do + case {Map.get(config, key1), Map.get(config, key2)} do + {{:error, _}, {:error, _}} -> config + {{:error, _}, _} -> Map.put(config, key1, nil) + {_, {:error, _}} -> Map.put(config, key2, nil) end end def uri_for_mode(:dev), do: @apns_development_api_uri def uri_for_mode(:prod), do: @apns_production_api_uri def uri_for_mode(_else), do: nil - - defp validate_file_path!(path) do - if :filelib.is_file(path), do: path, else: raise("file not found: #{path}") - end end diff --git a/lib/pigeon/apns/jwt_config.ex b/lib/pigeon/apns/jwt_config.ex index 0b21ac24..05d9d75e 100644 --- a/lib/pigeon/apns/jwt_config.ex +++ b/lib/pigeon/apns/jwt_config.ex @@ -101,7 +101,6 @@ defmodule Pigeon.APNS.JWTConfig do ...> ) %Pigeon.APNS.JWTConfig{uri: "api.push.apple.com", name: :test, team_id: "DEF1234567", key_identifier: "ABC1234567", - key: "test/support/AuthKey.p8-mock", keyfile: Path.expand("test/support/AuthKey.p8-mock"), ping_period: 300000, port: 2197, reconnect: false} @@ -118,14 +117,24 @@ defmodule Pigeon.APNS.JWTConfig do uri: Keyword.get(opts, :uri, ConfigParser.uri_for_mode(opts[:mode])), port: Keyword.get(opts, :port, 443), ping_period: Keyword.get(opts, :ping_period, 600_000), - key: opts[:key], + key: key(opts[:key]), keyfile: ConfigParser.file_path(opts[:key]), key_identifier: Keyword.get(opts, :key_identifier), team_id: Keyword.get(opts, :team_id) } + |> ConfigParser.strip_errors(:key, :keyfile) end def new(name) when is_atom(name), do: ConfigParser.parse(name) + + defp key(bin) when is_binary(bin) do + case :public_key.pem_decode(bin) do + [] -> {:error, {:invalid, bin}} + [{_, _, _}] -> bin + end + end + + defp key(other), do: {:error, {:invalid, other}} end defimpl Pigeon.Configurable, for: Pigeon.APNS.JWTConfig do @@ -176,8 +185,37 @@ defimpl Pigeon.Configurable, for: Pigeon.APNS.JWTConfig do defdelegate close(config), to: Shared - def connect_socket_options(%{key: nil}) do - {:error, :invalid_config} + def validate!(config) do + case config do + %{team_id: nil} -> + raise Pigeon.ConfigError, + reason: "attempted to start without valid team_id", + config: redact(config) + + %{key_identifier: nil} -> + raise Pigeon.ConfigError, + reason: "attempted to start without valid key_identifier", + config: redact(config) + + %{key: {:error, _}, keyfile: {:error, _}} -> + raise Pigeon.ConfigError, + reason: "attempted to start without valid key", + config: redact(config) + + _ -> + :ok + end + end + + defp redact(config) do + [:key, :keyfile] + |> Enum.reduce(config, fn key, acc -> + case Map.get(acc, key) do + bin when is_binary(bin) -> Map.put(acc, key, "[FILTERED]") + {:RSAPrivateKey, _bin} -> Map.put(acc, key, "[FILTERED]") + _ -> acc + end + end) end def connect_socket_options(%{key: _jwt_key} = config) do @@ -194,8 +232,6 @@ defimpl Pigeon.Configurable, for: Pigeon.APNS.JWTConfig do end @spec put_bearer_token(Config.headers(), JWTConfig.t()) :: Config.headers() - defp put_bearer_token(headers, %{key: nil}), do: headers - defp put_bearer_token(headers, config) do token_storage_key = config.key_identifier <> ":" <> config.team_id {timestamp, saved_token} = Pigeon.APNS.Token.get(token_storage_key) diff --git a/lib/pigeon/config_error.ex b/lib/pigeon/config_error.ex new file mode 100644 index 00000000..e99a7e7e --- /dev/null +++ b/lib/pigeon/config_error.ex @@ -0,0 +1,14 @@ +defmodule Pigeon.ConfigError do + defexception reason: nil, config: nil + + @impl true + def message(%{config: config, reason: reason}) do + """ + #{reason} + + The following configuration was given: + + #{inspect(config, pretty: true)} + """ + end +end diff --git a/lib/pigeon/configurable.ex b/lib/pigeon/configurable.ex index ce2cb30d..22a399d6 100644 --- a/lib/pigeon/configurable.ex +++ b/lib/pigeon/configurable.ex @@ -52,4 +52,7 @@ defprotocol Pigeon.Configurable do def schedule_ping(config) def close(config) + + @spec validate!(any) :: :ok + def validate!(config) end diff --git a/lib/pigeon/exceptions.ex b/lib/pigeon/exceptions.ex deleted file mode 100644 index 9cb18172..00000000 --- a/lib/pigeon/exceptions.ex +++ /dev/null @@ -1,10 +0,0 @@ -defmodule Pigeon.ConfigError do - @moduledoc """ - This error represents configuration errors: for example, configuring - both `:cert` and `:jwt_key` for APNS. - """ - - defexception [:message] - - @type t :: %__MODULE__{message: binary} -end diff --git a/lib/pigeon/fcm/config.ex b/lib/pigeon/fcm/config.ex index 0037d139..b609fb72 100644 --- a/lib/pigeon/fcm/config.ex +++ b/lib/pigeon/fcm/config.ex @@ -150,6 +150,22 @@ defimpl Pigeon.Configurable, for: Pigeon.FCM.Config do def close(_config) do end + def validate!(%{key: key}) when is_binary(key) do + :ok + end + + def validate!(config) do + raise Pigeon.ConfigError, + reason: "attempted to start without valid key", + config: redact(config) + end + + defp redact(%{key: key} = config) when is_binary(key) do + Map.put(config, :key, "[FILTERED]") + end + + defp redact(config), do: config + # no on_response callback, ignore def parse_result(_, _, nil, _notif), do: :ok diff --git a/lib/pigeon/fcm/notification.ex b/lib/pigeon/fcm/notification.ex index f5c41719..96126754 100644 --- a/lib/pigeon/fcm/notification.ex +++ b/lib/pigeon/fcm/notification.ex @@ -143,19 +143,11 @@ defmodule Pigeon.FCM.Notification do def new(reg_ids, notification, data) do reg_ids - |> chunk(@chunk_size, @chunk_size, []) + |> Enum.chunk_every(@chunk_size, @chunk_size, []) |> Enum.map(&new(&1, notification, data)) |> List.flatten() end - defp chunk(collection, chunk_size, step, padding) do - if Kernel.function_exported?(Enum, :chunk_every, 4) do - Enum.chunk_every(collection, chunk_size, step, padding) - else - Enum.chunk(collection, chunk_size, step, padding) - end - end - @doc """ Updates `"data"` key in push payload. diff --git a/lib/pigeon/worker.ex b/lib/pigeon/worker.ex index f22089ef..bd1eab6a 100644 --- a/lib/pigeon/worker.ex +++ b/lib/pigeon/worker.ex @@ -41,6 +41,7 @@ defmodule Pigeon.Worker do end def init({:ok, config}) do + Configurable.validate!(config) state = %Worker{config: config} {:producer, state} end diff --git a/mix.exs b/mix.exs index 59d72c71..ac18ff32 100644 --- a/mix.exs +++ b/mix.exs @@ -1,14 +1,14 @@ defmodule Pigeon.Mixfile do use Mix.Project - @version "1.4.0" + @version "1.5.0" def project do [ app: :pigeon, name: "Pigeon", version: @version, - elixir: "~> 1.4", + elixir: "~> 1.6", elixirc_paths: elixirc_paths(Mix.env()), source_url: "https://github.com/codedge-llc/pigeon", description: description(), @@ -51,7 +51,7 @@ defmodule Pigeon.Mixfile do [ {:poison, "~> 2.0 or ~> 3.0 or ~> 4.0"}, {:httpoison, "~> 0.7 or ~> 1.0"}, - {:gen_stage, "~> 0.12"}, + {:gen_stage, "~> 0.12 or ~> 1.0"}, {:joken, "~> 2.1"}, {:kadabra, "~> 0.4.3", optional: true}, {:earmark, "~> 1.0", only: :dev}, diff --git a/mix.lock b/mix.lock index c648028b..c95da718 100644 --- a/mix.lock +++ b/mix.lock @@ -1,33 +1,28 @@ %{ "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, - "certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, - "credo": {:hex, :credo, "1.0.2", "88bc918f215168bf6ce7070610a6173c45c82f32baa08bdfc80bf58df2d103b6", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "credo": {:hex, :credo, "1.2.2", "f57faf60e0a12b0ba9fd4bad07966057fde162b33496c509b95b027993494aab", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"}, - "dogma": {:hex, :dogma, "0.1.15", "5bceba9054b2b97a4adcb2ab4948ca9245e5258b883946e82d32f785340fd411", [:mix], [{:poison, ">= 2.0.0", [hex: :poison, optional: false]}]}, - "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, - "excoveralls": {:hex, :excoveralls, "0.10.5", "7c912c4ec0715a6013647d835c87cde8154855b9b84e256bc7a63858d5f284e3", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, - "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, - "gen_stage": {:hex, :gen_stage, "0.14.1", "9d46723fda072d4f4bb31a102560013f7960f5d80ea44dcb96fd6304ed61e7a4", [:mix], [], "hexpm"}, - "hackney": {:hex, :hackney, "1.15.0", "287a5d2304d516f63e56c469511c42b016423bcb167e61b611f6bad47e3ca60e", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, + "excoveralls": {:hex, :excoveralls, "0.12.2", "a513defac45c59e310ac42fcf2b8ae96f1f85746410f30b1ff2b710a4b6cd44b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "gen_stage": {:hex, :gen_stage, "1.0.0", "51c8ae56ff54f9a2a604ca583798c210ad245f415115453b773b621c49776df5", [:mix], [], "hexpm"}, + "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, "hpack": {:hex, :hpack_erl, "0.2.3", "17670f83ff984ae6cd74b1c456edde906d27ff013740ee4d9efaa4f1bf999633", [:rebar3], [], "hexpm"}, - "httpoison": {:hex, :httpoison, "1.5.0", "71ae9f304bdf7f00e9cd1823f275c955bdfc68282bc5eb5c85c3a9ade865d68e", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, - "joken": {:hex, :joken, "2.1.0", "bf21a73105d82649f617c5e59a7f8919aa47013d2519ebcc39d998d8d12adda9", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"}, - "jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, - "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"}, + "joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"}, + "jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm"}, "kadabra": {:hex, :kadabra, "0.4.4", "29d7f4c231d44a59d99b550fa75489a80a572140b3316809f326bf608bce33d8", [:mix], [{:hpack, "~> 0.2.3", [hex: :hpack_erl, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, - "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, - "pane": {:hex, :pane, "0.1.1", "4a9b46957a02991acbce012169ab7e8ecff74ad24886f94b142680062b10f167", [], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, - "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, - "scribe": {:hex, :scribe, "0.5.0", "1fe6dfffc4264a39633c1bd3fc9ce8377546974c40c7cd2a06194b2c2e91bba0", [], [{:pane, "~> 0.1", [hex: :pane, repo: "hexpm", optional: false]}], "hexpm"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, + "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, } diff --git a/secrets.tar.enc b/secrets.tar.enc index 7d2caf88..38468986 100644 Binary files a/secrets.tar.enc and b/secrets.tar.enc differ diff --git a/test/adm/config_test.exs b/test/adm/config_test.exs index db488370..8120497e 100644 --- a/test/adm/config_test.exs +++ b/test/adm/config_test.exs @@ -1,4 +1,25 @@ defmodule Pigeon.ADM.ConfigTest do use ExUnit.Case doctest Pigeon.ADM.Config, import: true + + @invalid_key_msg ~r/^attempted to start without valid client id and secret/ + + test "success if configured with client id and secret" do + config = Pigeon.ADM.Config.new(name: :test, client_id: "amzn1.iba-client.abc123", client_secret: "abc123") + {:ok, _} = Pigeon.ADM.Worker.init({:ok, config}) + end + + test "raises if configured with invalid client id" do + assert_raise(Pigeon.ConfigError, @invalid_key_msg, fn -> + config = Pigeon.ADM.Config.new(name: :test, client_id: nil, client_secret: "abc123") + Pigeon.ADM.Worker.init({:ok, config}) + end) + end + + test "raises if configured with invalid client secret" do + assert_raise(Pigeon.ConfigError, @invalid_key_msg, fn -> + config = Pigeon.ADM.Config.new(name: :test, client_id: "amzn1.iba-client.abc123", client_secret: nil) + Pigeon.ADM.Worker.init({:ok, config}) + end) + end end diff --git a/test/apns/config_test.exs b/test/apns/config_test.exs index 3548acbc..3324c2e3 100644 --- a/test/apns/config_test.exs +++ b/test/apns/config_test.exs @@ -1,4 +1,55 @@ defmodule Pigeon.APNS.ConfigTest do use ExUnit.Case doctest Pigeon.APNS.Config, import: true + + @invalid_cert_msg ~r/^attempted to start without valid certificate/ + @invalid_key_msg ~r/^attempted to start without valid key/ + + test "raises if configured with invalid cert raw text" do + assert_raise(Pigeon.ConfigError, @invalid_cert_msg, fn -> + config = + Pigeon.APNS.Config.new( + cert: "thisisnotacert", + key: "test/support/test_key.pem-mock" + ) + + Pigeon.Worker.init({:ok, config}) + end) + end + + test "raises if configured with invalid cert filepath" do + assert_raise(Pigeon.ConfigError, @invalid_cert_msg, fn -> + config = + Pigeon.APNS.Config.new( + cert: "does_not_exist.pem", + key: "test/support/test_key.pem-mock" + ) + + Pigeon.Worker.init({:ok, config}) + end) + end + + test "raises if configured with invalid key raw text" do + assert_raise(Pigeon.ConfigError, @invalid_key_msg, fn -> + config = + Pigeon.APNS.Config.new( + cert: "test/support/test_cert.pem-mock", + key: "thisisnotakey" + ) + + Pigeon.Worker.init({:ok, config}) + end) + end + + test "raises if configured with invalid key filepath" do + assert_raise(Pigeon.ConfigError, @invalid_key_msg, fn -> + config = + Pigeon.APNS.Config.new( + cert: "test/support/test_cert.pem-mock", + key: "does_not_exist.pem" + ) + + Pigeon.Worker.init({:ok, config}) + end) + end end diff --git a/test/apns/jwt_config_test.exs b/test/apns/jwt_config_test.exs index 4999b2ad..0fc611ac 100644 --- a/test/apns/jwt_config_test.exs +++ b/test/apns/jwt_config_test.exs @@ -2,6 +2,10 @@ defmodule Pigeon.APNS.JWTConfigTest do use ExUnit.Case doctest Pigeon.APNS.JWTConfig, import: true + @invalid_team_id_msg ~r/^attempted to start without valid team_id/ + @invalid_key_id_msg ~r/^attempted to start without valid key_identifier/ + @invalid_key_msg ~r/^attempted to start without valid key/ + def test_message(msg), do: "#{DateTime.to_string(DateTime.utc_now())} - #{msg}" @@ -18,4 +22,56 @@ defmodule Pigeon.APNS.JWTConfigTest do assert Pigeon.APNS.push(n, to: :apns_jwt_dynamic).response == :success end + + test "raises if configured with invalid key raw text" do + assert_raise(Pigeon.ConfigError, @invalid_key_msg, fn -> + config = + Pigeon.APNS.JWTConfig.new( + team_id: "ASDF1234", + key_identifier: "ASDF1234", + key: "notvalidkey" + ) + + Pigeon.Worker.init({:ok, config}) + end) + end + + test "raises if configured with invalid key file" do + assert_raise(Pigeon.ConfigError, @invalid_key_msg, fn -> + config = + Pigeon.APNS.JWTConfig.new( + team_id: "ASDF1234", + key_identifier: "ASDF1234", + key: "does_not_exist.p8" + ) + + Pigeon.Worker.init({:ok, config}) + end) + end + + test "raises if configured without team_id" do + assert_raise(Pigeon.ConfigError, @invalid_team_id_msg, fn -> + config = + Pigeon.APNS.JWTConfig.new( + team_id: nil, + key_identifier: "ASDF1234", + key: "test/support/AuthKey.p8-mock" + ) + + Pigeon.Worker.init({:ok, config}) + end) + end + + test "raises if configured without key_identifier" do + assert_raise(Pigeon.ConfigError, @invalid_key_id_msg, fn -> + config = + Pigeon.APNS.JWTConfig.new( + team_id: "ASDF1234", + key_identifier: nil, + key: "test/support/AuthKey.p8-mock" + ) + + Pigeon.Worker.init({:ok, config}) + end) + end end diff --git a/test/fcm/config_test.exs b/test/fcm/config_test.exs index 6d9785ff..71946488 100644 --- a/test/fcm/config_test.exs +++ b/test/fcm/config_test.exs @@ -1,4 +1,13 @@ defmodule Pigeon.FCM.ConfigTest do use ExUnit.Case doctest Pigeon.FCM.Config, import: true + + @invalid_key_msg ~r/^attempted to start without valid key/ + + test "raises if configured with invalid key" do + assert_raise(Pigeon.ConfigError, @invalid_key_msg, fn -> + config = Pigeon.FCM.Config.new(key: nil) + Pigeon.Worker.init({:ok, config}) + end) + end end