Skip to content

Commit

Permalink
feat: Choose a user during the Devtool login process (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
taorepoara authored Jan 18, 2024
1 parent 4abe251 commit c7444f0
Show file tree
Hide file tree
Showing 19 changed files with 213 additions and 26 deletions.
5 changes: 4 additions & 1 deletion server/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ config :dev_tools, DevTool.Endpoint,
render_errors: [view: DevTool.ErrorView, accepts: ~w(json), layout: false],
pubsub_server: DevTool.PubSub

config :dev_tools, DevTool.FakeHydra.Endpoint, secret_key_base: secret_key_base
config :dev_tools, DevTool.FakeHydra.Endpoint,
secret_key_base: secret_key_base,
render_errors: [view: DevTool.ErrorView, accepts: ~w(json), layout: false],
pubsub_server: DevTool.PubSub

config :cors_plug,
origin: "*",
Expand Down
8 changes: 7 additions & 1 deletion server/config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ config :dev_tools, DevTool.FakeHydra.Endpoint,
debug_errors: true,
code_reloader: true,
check_origin: false,
watchers: []
watchers: [],
live_reload: [
patterns: [
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
~r"lib/identity_web/templates/.*(eex)$"
]
]

config :dev_tools,
of_watchdog: System.get_env("OF_WATCHDOG_BIN", "/usr/bin/fwatchdog"),
Expand Down
25 changes: 22 additions & 3 deletions server/lib/dev_tool.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,24 @@ defmodule DevTool do

def controller do
quote do
use LenraCommonWeb, :controller
use Phoenix.Controller, namespace: DevTool

import LenraCommonWeb.ControllerHelpers
import Plug.Conn

alias DevTool.Router.Helpers, as: Routes
end
end

def view do
quote do
use LenraCommonWeb, :view
use Phoenix.View,
root: "lib/dev_tool/templates",
namespace: DevTool

# Import convenience functions from controllers
import Phoenix.Controller,
only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]

# Include shared imports and aliases for views
unquote(view_helpers())
Expand All @@ -51,8 +60,18 @@ defmodule DevTool do

defp view_helpers do
quote do
use LenraCommonWeb, :view_helpers
# Use all HTML functionality (forms, tags, etc)
use Phoenix.HTML

# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
import Phoenix.LiveView.Helpers

# Import basic rendering functionality (render, render_layout, etc)
import Phoenix.View

import DevTool.ErrorHelpers

# credo:disable-for-next-line Credo.Check.Readability.AliasAs
alias DevTool.Router.Helpers, as: Routes
end
end
Expand Down
12 changes: 7 additions & 5 deletions server/lib/dev_tool/app_adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule DevTool.AppAdapter do
"""
@behaviour ApplicationRunner.Adapter

alias DevTool.FakeHydra.OAuth2Helper
alias DevTool.FakeHydra.Oauth2Helper
alias DevTool.User
alias DevTool.UserServices

Expand Down Expand Up @@ -32,16 +32,18 @@ defmodule DevTool.AppAdapter do
end

def resource_from_params(%{"token" => token} = params) do
do_resource_from_params(1, token, params)
do_resource_from_params(nil, token, params)
end

def resource_from_params(_params) do
raise "No token found. Please add a token to your websocket request."
end

defp do_resource_from_params(user_id, token, params) do
with {:ok, _scope} <- OAuth2Helper.verify_scope(token, "app:websocket"),
{:ok, %User{id: id}} <- UserServices.upsert_fake_user(user_id) do
defp do_resource_from_params(forced_user_id, token, params) do
with {:ok, %{user: user_id}} <-
Oauth2Helper.claims_from_verified_token(token, "app:websocket"),
# The user id given in the socket is prioritized over the one in the token
{:ok, %User{id: id}} <- UserServices.upsert_fake_user(forced_user_id || user_id || 1) do
{:ok, id, "devtool-app", ApplicationRunner.AppSocket.extract_context(params)}
end
end
Expand Down
4 changes: 2 additions & 2 deletions server/lib/dev_tool/controllers/lenra_token_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ defmodule DevTool.LenraTokenController do
"""
use DevTool, :controller

alias DevTool.FakeHydra.OAuth2Helper
alias DevTool.FakeHydra.Oauth2Helper

def generate(conn, _opts) do
conn
|> send_resp(200, OAuth2Helper.generate_fake_token("app:websocket"))
|> send_resp(200, Oauth2Helper.generate_fake_token("app:websocket"))
|> halt()
end
end
46 changes: 39 additions & 7 deletions server/lib/dev_tool/fake_hydra/controllers/oauth2_controller.ex
Original file line number Diff line number Diff line change
@@ -1,20 +1,45 @@
defmodule DevTool.FakeHydra.OAuth2Controller do
defmodule DevTool.FakeHydra.Oauth2Controller do
alias DevTool.UserServices
use DevTool, :controller

alias DevTool.FakeHydra.OAuth2Helper
alias DevTool.FakeHydra.Oauth2Helper
alias DevTool.{Repo, User}
require Logger

def auth(conn, params) do
uri = params["redirect_uri"]
code = OAuth2Helper.generate_code(params["scope"])
encoded_params = URI.encode_query(%{"code" => code, "state" => params["state"]})
def auth(conn, %{"redirect_uri" => redirect_uri, "scope" => scope, "state" => state}) do
users =
Enum.map(Repo.all(User), fn user -> user.manual_id end)
|> Enum.sort()

if Enum.empty?(users) do
{:ok, user} = UserServices.upsert_fake_user(1)
users = [1]
end

next_id = Enum.max(users) + 1

render(
conn,
"choose-user.html",
users: users,
next_id: next_id,
redirect_uri: redirect_uri,
scope: scope,
state: state
)
end

def user(conn, %{"user" => user_id, "redirect_uri" => uri, "scope" => scope, "state" => state}) do
UserServices.upsert_fake_user(user_id)
code = Oauth2Helper.generate_code(scope, user_id)
encoded_params = URI.encode_query(%{"code" => code, "state" => state})
redirect(conn, external: "#{uri}?#{encoded_params}")
end

# defp encode_token(scope, )

def token(conn, %{"code" => code}) do
with {:ok, token, %{scope: scope}} <- OAuth2Helper.generate_token(code) do
with {:ok, token, %{scope: scope}} <- Oauth2Helper.generate_token(code) do
response = %{
access_token: token,
expire_in: 3600,
Expand All @@ -29,4 +54,11 @@ defmodule DevTool.FakeHydra.OAuth2Controller do
def revoke(conn, _params) do
resp(conn, 200, "ok")
end

def logout(conn, _params) do
render(
conn,
"logout.html"
)
end
end
7 changes: 7 additions & 0 deletions server/lib/dev_tool/fake_hydra/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ defmodule DevTool.FakeHydra.Endpoint do
key: "_dev_tool_key",
signing_salt: "ehlojLyn"
]
plug(Plug.Static,
at: "/",
from: {:dev_tools, "priv/static_hydra"},
gzip: false,
only: ~w(assets css fonts images favicon.ico robots.txt)
)

plug(DevTool.HealthCheck)

Expand All @@ -28,6 +34,7 @@ defmodule DevTool.FakeHydra.Endpoint do
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
plug(Phoenix.LiveReloader)
plug(Phoenix.CodeReloader)
end

Expand Down
18 changes: 14 additions & 4 deletions server/lib/dev_tool/fake_hydra/oauth2_helper.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule DevTool.FakeHydra.OAuth2Helper do
defmodule DevTool.FakeHydra.Oauth2Helper do
@moduledoc """
Helper function to create Oauth2 code/token and check them.
"""
Expand All @@ -8,8 +8,8 @@ defmodule DevTool.FakeHydra.OAuth2Helper do
@encrypt_salt "devtool_encrypt_salt"
@sign_salt "devtool_sign_salt"

def generate_code(scope) do
Token.encrypt(DevTool.FakeHydra.Endpoint, @encrypt_salt, %{scope: scope})
def generate_code(scope, user) do
Token.encrypt(DevTool.FakeHydra.Endpoint, @encrypt_salt, %{scope: scope, user: user})
end

def generate_token(code) do
Expand All @@ -19,7 +19,17 @@ defmodule DevTool.FakeHydra.OAuth2Helper do
end

def generate_fake_token(scope) do
Token.sign(DevTool.FakeHydra.Endpoint, @sign_salt, %{scope: scope})
Token.sign(DevTool.FakeHydra.Endpoint, @sign_salt, %{scope: scope, user: 1})
end

def claims_from_verified_token(token, needed_scope) do
with {:ok, %{scope: token_scope} = claims} <- claims_from_token(token),
true <- scopes_matches(token_scope, needed_scope) do
{:ok, claims}
else
false -> BusinessError.invalid_oauth2_scopes_tuple()
err -> err
end
end

def verify_scope(token, needed_scope) do
Expand Down
3 changes: 3 additions & 0 deletions server/lib/dev_tool/fake_hydra/oauth2_view.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule DevTool.FakeHydra.Oauth2View do
use DevTool, :view
end
18 changes: 15 additions & 3 deletions server/lib/dev_tool/fake_hydra/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,21 @@ defmodule DevTool.FakeHydra.Router do
"""
use DevTool, :router

pipeline :browser do
plug(:accepts, ["html"])
plug(:fetch_session)
# plug :fetch_live_flash
plug(:put_root_layout, {DevTool.LayoutView, :root})
# plug :protect_from_forgery
plug(:put_secure_browser_headers)
end

scope "/", DevTool.FakeHydra do
get("/oauth2/auth", OAuth2Controller, :auth)
post("/oauth2/token", OAuth2Controller, :token)
get("/oauth2/revoke", OAuth2Controller, :revoke)
pipe_through([:browser])
get("/oauth2/auth", Oauth2Controller, :auth)
get("/choose/user", Oauth2Controller, :user)
post("/oauth2/token", Oauth2Controller, :token)
post("/oauth2/revoke", Oauth2Controller, :revoke)
get("/oauth2/sessions/logout", Oauth2Controller, :logout)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<main class="arrows">
<nav>
<%= for user_id <- @users do %>
<a href={DevTool.FakeHydra.Router.Helpers.oauth2_path(@conn, :user, user: user_id, redirect_uri: @redirect_uri, scope: @scope, state: @state)}>User <%= user_id %></a>
<% end %>
</nav>
<form action={DevTool.FakeHydra.Router.Helpers.oauth2_path(@conn, :user)}>
<input type="number" name="user" value={@next_id} min="1" step="1"/>
<input type="hidden" name="redirect_uri" value={@redirect_uri}/>
<input type="hidden" name="scope" value={@scope}/>
<input type="hidden" name="state" value={@state}/>
<button type="submit">Select</button>
</form>
</main>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script>
window.close();
</script>
1 change: 1 addition & 0 deletions server/lib/dev_tool/templates/layout/app.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= @inner_content %>
15 changes: 15 additions & 0 deletions server/lib/dev_tool/templates/layout/root.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<%= live_title_tag assigns[:page_title] || "Lenra" %>
<link rel="icon" type="image/png" href="/images/favicon.png"/>
<link rel="apple-touch-icon" href="/images/appicon.png"/>
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/css/style.css")} media="print" onload='this.media="all"'/>
</head>
<body>
<%= @inner_content %>
</body>
</html>
File renamed without changes.
7 changes: 7 additions & 0 deletions server/lib/dev_tool/views/layout_view.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule DevTool.LayoutView do
use DevTool, :view

# Phoenix LiveDashboard is available only in development by default,
# so we instruct Elixir to not warn if the dashboard route is missing.
@compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
end
3 changes: 3 additions & 0 deletions server/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ defmodule DevTool.MixProject do
{:excoveralls, "~> 0.10", only: :test},
{:bypass, "~> 2.0", only: :test},
{:phoenix, "~> 1.6.15"},
{:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.17.5"},
{:telemetry, "~> 0.4", override: true},
{:telemetry_metrics, "~> 0.4"},
{:telemetry_poller, "~> 0.4"},
Expand Down
3 changes: 3 additions & 0 deletions server/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
"phoenix": {:hex, :phoenix, "1.6.16", "e5bdd18c7a06da5852a25c7befb72246de4ddc289182285f8685a40b7b5f5451", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e15989ff34f670a96b95ef6d1d25bad0d9c50df5df40b671d8f4a669e050ac39"},
"phoenix_html": {:hex, :phoenix_html, "3.3.3", "380b8fb45912b5638d2f1d925a3771b4516b9a78587249cabe394e0a5d579dc9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "923ebe6fec6e2e3b3e569dfbdc6560de932cd54b000ada0208b5f45024bdd76c"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.17.14", "5ec615d4d61bf9d4755f158bd6c80372b715533fe6d6219e12d74fb5eedbeac1", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.0 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "afeb6ba43ce329a6f7fc1c9acdfc6d3039995345f025febb7f409a92f6faebd3"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"},
"phoenix_view": {:hex, :phoenix_view, "2.0.3", "4d32c4817fce933693741deeb99ef1392619f942633dde834a5163124813aad3", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "cd34049af41be2c627df99cd4eaa71fc52a328c0c3d8e7d4aa28f880c30e7f64"},
Expand Down
47 changes: 47 additions & 0 deletions server/priv/static_hydra/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
html {
height: 100%;
}

body {
height: 100%;
}

main {
margin: auto;
width: 100%;
max-width: 40rem;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2rem;
}

nav {
--size: 5rem;
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
list-style-type: none;
padding: 0;
margin: 0;
width: 100%;
max-width: 40rem;
overflow: auto;
}

nav a {
display: flex;
align-items: center;
justify-content: center;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 1rem;
text-decoration: none;
color: #333;

width: var(--size);
height: var(--size);
}

0 comments on commit c7444f0

Please sign in to comment.