Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: partner api - allow impersonation of user #1883

Merged
merged 2 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading