diff --git a/lib/logflare/partners.ex b/lib/logflare/partners.ex index e9c0e1c38..2dbdbf2b3 100644 --- a/lib/logflare/partners.ex +++ b/lib/logflare/partners.ex @@ -80,8 +80,7 @@ defmodule Logflare.Partners do query = from(p in Partner, join: u in assoc(p, :users), - where: p.token == ^token, - where: u.token == ^user_token, + where: p.token == ^token and u.token == ^user_token, select: u ) diff --git a/lib/logflare/partners/cache.ex b/lib/logflare/partners/cache.ex index fd97782c3..c186e5c69 100644 --- a/lib/logflare/partners/cache.ex +++ b/lib/logflare/partners/cache.ex @@ -11,6 +11,7 @@ defmodule Logflare.Partners.Cache do end def get_partner(id), do: apply_repo_fun(__ENV__.function, [id]) + def get_user_by_token(partner, token), do: apply_repo_fun(__ENV__.function, [partner, token]) defp apply_repo_fun(arg1, arg2) do Logflare.ContextCache.apply_fun(Partners, arg1, arg2) diff --git a/lib/logflare_web/controllers/plugs/verify_api_access.ex b/lib/logflare_web/controllers/plugs/verify_api_access.ex index c219a2b59..2ecf270b5 100644 --- a/lib/logflare_web/controllers/plugs/verify_api_access.ex +++ b/lib/logflare_web/controllers/plugs/verify_api_access.ex @@ -13,6 +13,7 @@ defmodule LogflareWeb.Plugs.VerifyApiAccess do alias Logflare.Users alias Logflare.User alias Logflare.Partners.Partner + alias Logflare.Partners alias LogflareWeb.Api.FallbackController def init(args), do: args |> Enum.into(%{}) @@ -20,11 +21,34 @@ defmodule LogflareWeb.Plugs.VerifyApiAccess do def call(conn, opts) do opts = Enum.into(opts, %{scopes: []}) resource_type = Map.get(conn.assigns, :resource_type) - partner_scope = "partner" in opts.scopes + + impersonate_user_token = get_req_header(conn, "x-lf-partner-user") |> List.first() # generic access - case identify_requestor(conn, opts.scopes) do - {:ok, %Partner{} = partner} when partner_scope == true -> - assign(conn, :partner, partner) + scopes_to_check = + if impersonate_user_token != nil do + ~w(partner) + else + opts.scopes + end + + case identify_requestor(conn, scopes_to_check) do + {:ok, %Partner{} = partner} when impersonate_user_token == nil -> + conn + |> assign(:partner, partner) + + {:ok, %Partner{} = partner} when impersonate_user_token != nil -> + # maybe get the user target + + Partners.Cache.get_user_by_token(partner, impersonate_user_token) + |> then(fn + %User{} = u -> + conn + |> assign(:partner, partner) + |> assign(:user, Users.Cache.preload_defaults(u)) + + _ -> + FallbackController.call(conn, {:error, :unauthorized}) + end) {:ok, %User{} = user} -> assign(conn, :user, user) diff --git a/test/logflare_web/plugs/verify_api_access_test.exs b/test/logflare_web/plugs/verify_api_access_test.exs index 36c638a63..17d1fd4ee 100644 --- a/test/logflare_web/plugs/verify_api_access_test.exs +++ b/test/logflare_web/plugs/verify_api_access_test.exs @@ -20,8 +20,35 @@ defmodule LogflareWeb.Plugs.VerifyApiAccessTest do ] end + describe "partner impersonation" do + setup do + conn = build_conn(:post, "/api/sources", %{"name" => "some name"}) + + {:ok, conn: conn} + end + test "can impersonate partner-provisioned user", %{conn: conn, user: user} do + partner = insert(:partner, users: [user]) + + conn + |> put_req_header("x-lf-partner-user", user.token) + |> add_partner_access_token(partner) + |> VerifyApiAccess.call(%{}) + |> assert_authorized(user) + + + partner = insert(:partner) + conn + |> put_req_header("x-lf-partner-user", user.token) + |> add_partner_access_token(partner) + |> VerifyApiAccess.call(%{}) + |> assert_unauthorized() + + end + + end + describe "source ingestion auth" do - setup %{source: source} do + setup %{source: source} do conn = build_conn(:post, "/logs", %{"source" => Atom.to_string(source.token)}) |> assign(:source, source)