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(%{