Skip to content

Commit

Permalink
Merge pull request #1883 from Logflare/feat/partner-impersonate-user
Browse files Browse the repository at this point in the history
feat: partner api - allow impersonation of user
  • Loading branch information
Ziinc authored Dec 14, 2023
2 parents 241f647 + ced4ef3 commit 26c325d
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 7 deletions.
3 changes: 1 addition & 2 deletions lib/logflare/partners.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
1 change: 1 addition & 0 deletions lib/logflare/partners/cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
32 changes: 28 additions & 4 deletions lib/logflare_web/controllers/plugs/verify_api_access.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,42 @@ 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(%{})

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)
Expand Down
29 changes: 28 additions & 1 deletion test/logflare_web/plugs/verify_api_access_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 26c325d

Please sign in to comment.