From 41d6aff8ebe2c49165b54a0faaa36e459a174e6d Mon Sep 17 00:00:00 2001 From: michaeljguarino Date: Fri, 31 Jan 2025 11:51:23 -0500 Subject: [PATCH] Add query to fetch license keys for enterprise accounts (#1402) --- apps/core/lib/core/services/accounts.ex | 14 ++++++++++ apps/core/lib/core/services/payments.ex | 5 ++++ apps/core/test/services/accounts_test.exs | 27 +++++++++++++++++++ apps/graphql/lib/graphql/resolvers/account.ex | 3 +++ apps/graphql/lib/graphql/schema/account.ex | 6 +++++ .../test/queries/account_queries_test.exs | 16 +++++++++++ schema/schema.graphql | 2 ++ www/src/generated/graphql.ts | 1 + 8 files changed, 74 insertions(+) diff --git a/apps/core/lib/core/services/accounts.ex b/apps/core/lib/core/services/accounts.ex index 4b5385b372..a27da50f6b 100644 --- a/apps/core/lib/core/services/accounts.ex +++ b/apps/core/lib/core/services/accounts.ex @@ -418,6 +418,20 @@ defmodule Core.Services.Accounts do do: allow(service_account, user, :impersonate) + @doc """ + Grabs the license key for a users account + """ + @spec license_key(User.t) :: {:ok, binary} | {:error, term} + def license_key(%User{} = user) do + with {:ent, true} <- {:ent, Payments.enterprise?(user)}, + {:ok, _} <- Payments.allow_billing(user) do + license_key() + else + {:ent, _} -> {:error, "only enterprise accounts can download license keys"} + err -> err + end + end + def license_key() do exp = Timex.now() |> Timex.shift(days: 365) |> Timex.to_unix() with {:ok, claims} <- Jwt.generate_claims(%{"enterprise" => true, "exp" => exp}), diff --git a/apps/core/lib/core/services/payments.ex b/apps/core/lib/core/services/payments.ex index 204723d813..a0ca55d4d8 100644 --- a/apps/core/lib/core/services/payments.ex +++ b/apps/core/lib/core/services/payments.ex @@ -128,6 +128,11 @@ defmodule Core.Services.Payments do def allow(%Subscription{} = subscription, %User{} = user), do: allow(subscription, user, :access) + def allow_billing(%User{} = user) do + %{account: account} = Repo.preload(user, [:account]) + allow(account, user, :pay) + end + @doc """ List all invoices against a subscription """ diff --git a/apps/core/test/services/accounts_test.exs b/apps/core/test/services/accounts_test.exs index 5b5b30f7b3..b156a0e3a5 100644 --- a/apps/core/test/services/accounts_test.exs +++ b/apps/core/test/services/accounts_test.exs @@ -794,4 +794,31 @@ defmodule Core.Services.AccountsTest do assert updated.usage_updated end end + + describe "#license_key/1" do + test "enterprise billing users can download license keys" do + account = insert(:account, billing_customer_id: "cus_id", user_count: 2, cluster_count: 0) + user = insert(:user, roles: %{admin: true}, account: account) + enterprise_plan(account) + + {:ok, key} = Accounts.license_key(user) + + assert is_binary(key) + end + + test "non billing users cannot downlod license keys" do + account = insert(:account, billing_customer_id: "cus_id", user_count: 2, cluster_count: 0) + user = insert(:user, account: account) + enterprise_plan(account) + + {:error, _} = Accounts.license_key(user) + end + + test "non enterprise accounts cannot downlod license keys" do + account = insert(:account, billing_customer_id: "cus_id", user_count: 2, cluster_count: 0) + user = insert(:user, roles: %{admin: true}, account: account) + + {:error, _} = Accounts.license_key(user) + end + end end diff --git a/apps/graphql/lib/graphql/resolvers/account.ex b/apps/graphql/lib/graphql/resolvers/account.ex index 7bcbb76573..cf2874a1e2 100644 --- a/apps/graphql/lib/graphql/resolvers/account.ex +++ b/apps/graphql/lib/graphql/resolvers/account.ex @@ -88,6 +88,9 @@ defmodule GraphQl.Resolvers.Account do def resolve_invite(%{id: secure_id}, _), do: {:ok, Accounts.get_invite(secure_id)} + def license_key(_, %{context: %{current_user: user}}), + do: Accounts.license_key(user) + def create_service_account(%{attributes: attrs}, %{context: %{current_user: user}}), do: Accounts.create_service_account(attrs, user) diff --git a/apps/graphql/lib/graphql/schema/account.ex b/apps/graphql/lib/graphql/schema/account.ex index 57204fdae5..6420e47ef5 100644 --- a/apps/graphql/lib/graphql/schema/account.ex +++ b/apps/graphql/lib/graphql/schema/account.ex @@ -302,6 +302,12 @@ defmodule GraphQl.Schema.Account do resolve &Account.list_oauth_integrations/2 end + + field :license_key, :string do + middleware Authenticated + + resolve &Account.license_key/2 + end end object :account_mutations do diff --git a/apps/graphql/test/queries/account_queries_test.exs b/apps/graphql/test/queries/account_queries_test.exs index b3f755723b..8773a84d2b 100644 --- a/apps/graphql/test/queries/account_queries_test.exs +++ b/apps/graphql/test/queries/account_queries_test.exs @@ -329,4 +329,20 @@ defmodule GraphQl.AccountQueriesTest do |> ids_equal(invites) end end + + describe "licenseKey" do + test "enterprise billing users can download license keys" do + account = insert(:account, billing_customer_id: "cus_id", user_count: 2, cluster_count: 0) + user = insert(:user, roles: %{admin: true}, account: account) + enterprise_plan(account) + + {:ok, %{data: %{"licenseKey" => k}}} = run_query(""" + query { + licenseKey + } + """, %{}, %{current_user: user}) + + assert is_binary(k) + end + end end diff --git a/schema/schema.graphql b/schema/schema.graphql index fa2a2b877a..78859ba607 100644 --- a/schema/schema.graphql +++ b/schema/schema.graphql @@ -130,6 +130,8 @@ type RootQueryType { oauthIntegrations: [OauthIntegration] + licenseKey: String + incidents( after: String, first: Int, before: String, last: Int, repositoryId: ID, supports: Boolean, q: String, sort: IncidentSort, order: Order, filters: [IncidentFilter] ): IncidentConnection diff --git a/www/src/generated/graphql.ts b/www/src/generated/graphql.ts index 320c33ccdc..6a24ae4a6c 100644 --- a/www/src/generated/graphql.ts +++ b/www/src/generated/graphql.ts @@ -3806,6 +3806,7 @@ export type RootQueryType = { invoices?: Maybe; keyBackup?: Maybe; keyBackups?: Maybe; + licenseKey?: Maybe; loginMethod?: Maybe; loginMetrics?: Maybe>>; me?: Maybe;