From 669490e9178b6d9085065b533caa8f59f4457a24 Mon Sep 17 00:00:00 2001 From: Rafael Scheffer Date: Mon, 6 Sep 2021 14:59:01 -0300 Subject: [PATCH] Allow fetching custom token defined in config --- README.md | 30 +++++++++++++++++++ lib/waffle/storage/google/cloud_storage.ex | 22 ++++++++------ .../storage/google/token/default_fetcher.ex | 11 +++++++ lib/waffle/storage/google/token/fetcher.ex | 3 ++ 4 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 lib/waffle/storage/google/token/default_fetcher.ex create mode 100644 lib/waffle/storage/google/token/fetcher.ex diff --git a/README.md b/README.md index 898dae3..0f175f3 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,36 @@ module (e.g. `def bucket(), do: "my-bucket"`). Authentication is done through Goth which requires credentials (https://github.com/peburrows/goth#installation). +### Custom Token Generation ### + +By default, the credentials provided to Goth will be used to generate tokens. +If you have multiple sets of credentials in Goth or otherwise need more control +over token generation, you can define your own module: + +```elixir +defmodule MyCredentials do + @behaviour Waffle.Storage.Google.TokenFetcher + + @impl Waffle.Storage.Google.TokenFetcher + def get_token(scopes) when is_list(scopes), do: get_token(Enum.join(scopes, " ")) + + @impl Waffle.Storage.Google.TokenFetcher + def get_token(scope) when is_binary(scope) do + {:ok, token} = Goth.Token.for_scope({"my-user@my-gcs-account.com", scope}) + token.token + end +end +``` + +And configure it to use this new module instead of the default token generation: + +```elixir +config :waffle, + storage: Waffle.Storage.Google, + bucket: "gcs-bucket-name", + token_fetcher: MyCredentials +``` + ## URL Signing If your bucket/object permissions do not allow for public access, you will need diff --git a/lib/waffle/storage/google/cloud_storage.ex b/lib/waffle/storage/google/cloud_storage.ex index c55ad6a..90c0aa2 100644 --- a/lib/waffle/storage/google/cloud_storage.ex +++ b/lib/waffle/storage/google/cloud_storage.ex @@ -14,8 +14,6 @@ defmodule Waffle.Storage.Google.CloudStorage do otherwise some (or all) calls may fail. """ - @full_control_scope "https://www.googleapis.com/auth/devstorage.full_control" - alias GoogleApi.Storage.V1.Connection alias GoogleApi.Storage.V1.Api.Objects alias GoogleApi.Storage.V1.Model.Object @@ -39,7 +37,9 @@ defmodule Waffle.Storage.Google.CloudStorage do |> Keyword.put(:acl, acl) |> Enum.into(%{}) - insert(conn(), bucket(definition), path, data(meta), gcs_options) + meta + |> conn() + |> insert(bucket(definition), path, data(meta), gcs_options) end @doc """ @@ -47,8 +47,9 @@ defmodule Waffle.Storage.Google.CloudStorage do """ @spec put(Types.definition, Types.version, Types.meta) :: object_or_error def delete(definition, version, meta) do - Objects.storage_objects_delete( - conn(), + meta + |> conn() + |> Objects.storage_objects_delete( bucket(definition), path_for(definition, version, meta) |> URI.encode_www_form() ) @@ -71,10 +72,13 @@ defmodule Waffle.Storage.Google.CloudStorage do Constructs a new connection object with scoped authentication. If no scope is provided, the `devstorage.full_control` scope is used as a default. """ - @spec conn(String.t) :: Tesla.Env.client - def conn(scope \\ @full_control_scope) do - {:ok, token} = Goth.Token.for_scope(scope) - Connection.new(token.token) + @spec conn(Types.meta) :: Tesla.Env.client + def conn(scope) do + token_store = Application.get_env(:waffle, :token_fetcher, Waffle.Storage.Google.Token.DefaultFetcher) + + scope + |> token_store.get_token() + |> Connection.new() end @doc """ diff --git a/lib/waffle/storage/google/token/default_fetcher.ex b/lib/waffle/storage/google/token/default_fetcher.ex new file mode 100644 index 0000000..5033462 --- /dev/null +++ b/lib/waffle/storage/google/token/default_fetcher.ex @@ -0,0 +1,11 @@ +defmodule Waffle.Storage.Google.Token.DefaultFetcher do + @behaviour Waffle.Storage.Google.Token.Fetcher + + @full_control_scope "https://www.googleapis.com/auth/devstorage.full_control" + + @impl Waffle.Storage.Google.Token.Fetcher + def get_token(_scope) do + {:ok, token} = Goth.Token.for_scope(@full_control_scope) + token.token + end +end diff --git a/lib/waffle/storage/google/token/fetcher.ex b/lib/waffle/storage/google/token/fetcher.ex new file mode 100644 index 0000000..3e0982d --- /dev/null +++ b/lib/waffle/storage/google/token/fetcher.ex @@ -0,0 +1,3 @@ +defmodule Waffle.Storage.Google.Token.Fetcher do + @callback get_token(Waffle.Types.meta()) :: binary() +end