diff --git a/assets/src/App.js b/assets/src/App.js index b236ce60a2..c8c3386cca 100644 --- a/assets/src/App.js +++ b/assets/src/App.js @@ -13,6 +13,7 @@ import { OAuthCallback } from './components/OauthCallback' import 'react-toggle/style.css' import 'react-pulse-dot/dist/index.css' import { client } from './helpers/client' +import { LinkLogin } from './components/LinkLogin' const INTERCOM_APP_ID = 'p127zb9y' @@ -27,6 +28,10 @@ export default function App() { path="/login" component={Login} /> + { + setToken(jwt) + window.location = '/' + }, + onError: console.log, + }) + + useEffect(() => { + mutation() + }, []) + + if (error) { + return ( + + + This login link is invalid + + + ) + } + + return ( + + + + ) +} diff --git a/assets/src/components/graphql/users.js b/assets/src/components/graphql/users.js index a9585a650e..c22874638c 100644 --- a/assets/src/components/graphql/users.js +++ b/assets/src/components/graphql/users.js @@ -164,6 +164,16 @@ export const SIGNUP = gql` ${UserFragment} ` +export const LOGIN_LINK = gql` + mutation Link($key: String!) { + loginLink(key: $key) { + ...UserFragment + jwt + } + } + ${UserFragment} +` + export const NOTIFICATIONS_Q = gql` query Notifs($all: Boolean, $cursor: String) { notifications(all: $all, after: $cursor, first: 50) { diff --git a/config/config.exs b/config/config.exs index 29726877ce..a70fcd20e3 100644 --- a/config/config.exs +++ b/config/config.exs @@ -75,4 +75,6 @@ config :console, Console.PartitionedCache, allocated_memory: 1000 * 1000 * 500 ] +config :console, :login_link, [] + import_config "#{Mix.env()}.exs" diff --git a/config/test.exs b/config/test.exs index 7fd5d3822b..dfffb7a109 100644 --- a/config/test.exs +++ b/config/test.exs @@ -30,6 +30,10 @@ config :console, git_ssh_key: :pass, grafana_dns: "grafana.example.com" +config :console, :login_link, + key: "test-key", + email: "sandbox@plural.sh" + config :console, :consumers, [Console.EchoConsumer] config :kazan, :server, %{url: "kubernetes.default", auth: %{token: "your_token"}} diff --git a/lib/console/graphql/resolvers/user.ex b/lib/console/graphql/resolvers/user.ex index a40964f381..c85b7a4a0d 100644 --- a/lib/console/graphql/resolvers/user.ex +++ b/lib/console/graphql/resolvers/user.ex @@ -9,6 +9,18 @@ defmodule Console.GraphQl.Resolvers.User do def query(RoleBinding, _), do: RoleBinding def query(_, _), do: User + def login_link(%{key: k}, _) do + Console.conf(:login_link) + |> Map.new() + |> case do + %{key: ^k, email: email} when is_binary(email) -> + Users.get_user_by_email(email) + |> ok() + |> with_jwt() + _ -> {:error, "unauthorized"} + end + end + def list_users(args, _) do User.ordered() |> maybe_search(User, args) diff --git a/lib/console/graphql/users.ex b/lib/console/graphql/users.ex index 8659332a76..8e2c62a691 100644 --- a/lib/console/graphql/users.ex +++ b/lib/console/graphql/users.ex @@ -182,7 +182,7 @@ defmodule Console.GraphQl.Users do field :role, :role do middleware Authenticated - resolve safe_resolver(&User.resolve_role/2) + safe_resolve &User.resolve_role/2 end connection field :roles, node_type: :role do @@ -205,13 +205,20 @@ defmodule Console.GraphQl.Users do arg :email, non_null(:string) arg :password, non_null(:string) - resolve safe_resolver(&User.signin_user/2) + safe_resolve &User.signin_user/2 + end + + field :login_link, :user do + middleware AllowJwt + arg :key, non_null(:string) + + safe_resolve &User.login_link/2 end field :read_notifications, :user do middleware Authenticated - resolve safe_resolver(&User.read_notifications/2) + safe_resolve &User.read_notifications/2 end field :signup, :user do @@ -220,7 +227,7 @@ defmodule Console.GraphQl.Users do arg :invite_id, non_null(:string) arg :attributes, non_null(:user_attributes) - resolve safe_resolver(&User.signup_user/2) + safe_resolve &User.signup_user/2 end field :oauth_callback, :user do @@ -228,7 +235,7 @@ defmodule Console.GraphQl.Users do arg :code, non_null(:string) arg :redirect, :string - resolve safe_resolver(&User.oauth_callback/2) + safe_resolve &User.oauth_callback/2 end field :create_invite, :invite do @@ -236,7 +243,7 @@ defmodule Console.GraphQl.Users do middleware Sandboxed arg :attributes, non_null(:invite_attributes) - resolve safe_resolver(&User.create_invite/2) + safe_resolve &User.create_invite/2 end field :update_user, :user do @@ -245,7 +252,7 @@ defmodule Console.GraphQl.Users do arg :id, :id arg :attributes, non_null(:user_attributes) - resolve safe_resolver(&User.update_user/2) + safe_resolve &User.update_user/2 end field :create_group, :group do @@ -254,7 +261,7 @@ defmodule Console.GraphQl.Users do middleware Sandboxed arg :attributes, non_null(:group_attributes) - resolve safe_resolver(&User.create_group/2) + safe_resolve &User.create_group/2 end field :delete_group, :group do @@ -263,7 +270,7 @@ defmodule Console.GraphQl.Users do middleware Sandboxed arg :group_id, non_null(:id) - resolve safe_resolver(&User.delete_group/2) + safe_resolve &User.delete_group/2 end field :update_group, :group do @@ -273,7 +280,7 @@ defmodule Console.GraphQl.Users do arg :group_id, non_null(:id) arg :attributes, non_null(:group_attributes) - resolve safe_resolver(&User.update_group/2) + safe_resolve &User.update_group/2 end field :create_group_member, :group_member do @@ -283,7 +290,7 @@ defmodule Console.GraphQl.Users do arg :group_id, non_null(:id) arg :user_id, non_null(:id) - resolve safe_resolver(&User.create_group_member/2) + safe_resolve &User.create_group_member/2 end field :delete_group_member, :group_member do @@ -293,7 +300,7 @@ defmodule Console.GraphQl.Users do arg :group_id, non_null(:id) arg :user_id, non_null(:id) - resolve safe_resolver(&User.delete_group_member/2) + safe_resolve &User.delete_group_member/2 end field :create_role, :role do @@ -302,7 +309,7 @@ defmodule Console.GraphQl.Users do middleware Sandboxed arg :attributes, non_null(:role_attributes) - resolve safe_resolver(&User.create_role/2) + safe_resolve &User.create_role/2 end field :update_role, :role do @@ -312,7 +319,7 @@ defmodule Console.GraphQl.Users do arg :id, non_null(:id) arg :attributes, non_null(:role_attributes) - resolve safe_resolver(&User.update_role/2) + safe_resolve &User.update_role/2 end field :delete_role, :role do @@ -321,7 +328,7 @@ defmodule Console.GraphQl.Users do middleware Sandboxed arg :id, non_null(:id) - resolve safe_resolver(&User.delete_role/2) + safe_resolve &User.delete_role/2 end end diff --git a/plural/helm/console/Chart.yaml b/plural/helm/console/Chart.yaml index 9c50d86523..9b9f8ca3be 100644 --- a/plural/helm/console/Chart.yaml +++ b/plural/helm/console/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 appVersion: 0.3.6 description: A chart for plural console name: console -version: 0.7.28 +version: 0.7.30 dependencies: - name: test-base repository: https://pluralsh.github.io/module-library diff --git a/plural/helm/console/templates/secrets.yaml b/plural/helm/console/templates/secrets.yaml index d72d338bfa..49e179bcea 100644 --- a/plural/helm/console/templates/secrets.yaml +++ b/plural/helm/console/templates/secrets.yaml @@ -35,6 +35,9 @@ data: {{ if .Values.is_demo }} IS_DEMO_PROJECT: 'true' {{ end }} +{{- range $key, $value := .Values.extraSecretEnv -}} + {{ $key }}: {{ $value | quote }} +{{- end -}} {{ if .Values.secrets.id_rsa }} --- apiVersion: v1 diff --git a/plural/helm/console/values.yaml b/plural/helm/console/values.yaml index fab2882c78..2c5663e2b7 100644 --- a/plural/helm/console/values.yaml +++ b/plural/helm/console/values.yaml @@ -13,6 +13,8 @@ serviceAccount: create: true annotations: {} +extraSecretEnv: [] + configOverlays: - name: console-cpu labels: diff --git a/rel/config/console.exs b/rel/config/console.exs index c39c0882ba..9033c4b30a 100644 --- a/rel/config/console.exs +++ b/rel/config/console.exs @@ -85,3 +85,9 @@ if String.starts_with?(git_url, "https") do config :console, git_ssh_key: :pass end + +if !!get_env("CONSOLE_LOGIN_KEY") and get_env("CONSOLE_LOGIN_EMAIL") do + config :console, :login_link, + key: get_env("CONSOLE_LOGIN_KEY"), + email: get_env("CONSOLE_LOGIN_EMAIL") +end diff --git a/test/console/graphql/mutations/user_mutations_test.exs b/test/console/graphql/mutations/user_mutations_test.exs index 8d9e50f31f..7bfbb1c394 100644 --- a/test/console/graphql/mutations/user_mutations_test.exs +++ b/test/console/graphql/mutations/user_mutations_test.exs @@ -22,6 +22,45 @@ defmodule Console.GraphQl.UserMutationsTest do end end + describe "loginLink" do + test "A user can be signed in via a random link" do + {:ok, user} = Users.create_user(%{ + name: "some user", + email: "sandbox@plural.sh", + password: "bogus password" + }) + + {:ok, %{data: %{"loginLink" => signin}}} = run_query(""" + mutation Link($key: String!) { + loginLink(key: $key) { + id + jwt + } + } + """, %{"key" => "test-key"}) + + assert signin["id"] == user.id + assert signin["jwt"] + end + + test "If links are invalid signin won't work" do + {:ok, _} = Users.create_user(%{ + name: "some user", + email: "sandbox@plural.sh", + password: "bogus password" + }) + + {:ok, %{data: %{"loginLink" => nil}, errors: [_ | _]}} = run_query(""" + mutation Link($key: String!) { + loginLink(key: $key) { + id + jwt + } + } + """, %{"key" => "invalid"}) + end + end + describe "updateUser" do test "It can update a user's attributes" do {:ok, user} = Users.create_user(%{