diff options
author | href <href@random.sh> | 2021-09-07 05:28:02 +0200 |
---|---|---|
committer | href <href@random.sh> | 2021-09-07 05:28:02 +0200 |
commit | cf7b21afa87ca5202bd626d26a60adab1e9131a1 (patch) | |
tree | 245cbb332e9ab63a2b84e4168e0ce750dc1e7ae9 /lib | |
parent | user_track: fix find_by_account/2 (diff) |
web: openid & various fixes
Diffstat (limited to '')
-rw-r--r-- | lib/lsg_web.ex | 6 | ||||
-rw-r--r-- | lib/lsg_web/context_plug.ex | 12 | ||||
-rw-r--r-- | lib/lsg_web/controllers/irc_controller.ex | 7 | ||||
-rw-r--r-- | lib/lsg_web/controllers/open_id_controller.ex | 64 | ||||
-rw-r--r-- | lib/lsg_web/live/chat_live.ex | 7 | ||||
-rw-r--r-- | lib/lsg_web/router.ex | 6 | ||||
-rw-r--r-- | lib/lsg_web/templates/alcoolog/auth.html.eex | 12 | ||||
-rw-r--r-- | lib/lsg_web/templates/irc/index.html.eex | 15 | ||||
-rw-r--r-- | lib/lsg_web/templates/layout/app.html.eex | 4 | ||||
-rw-r--r-- | lib/lsg_web/templates/open_id/error.html.eex | 3 | ||||
-rw-r--r-- | lib/lsg_web/templates/page/user.html.eex | 5 | ||||
-rw-r--r-- | lib/lsg_web/views/open_id_view.ex | 4 |
12 files changed, 124 insertions, 21 deletions
diff --git a/lib/lsg_web.ex b/lib/lsg_web.ex index eb0cdc5..3d9ab9a 100644 --- a/lib/lsg_web.ex +++ b/lib/lsg_web.ex @@ -29,12 +29,15 @@ defmodule LSGWeb do chan end + def format_chan(chan = "!"<>_), do: chan + def reformat_chan("♯") do "#" end def reformat_chan("♯♯") do "##" end + def reformat_chan(chan = "!"<>_), do: chan def reformat_chan(chan) do "#"<>chan @@ -46,6 +49,7 @@ defmodule LSGWeb do import Plug.Conn import LSGWeb.Router.Helpers import LSGWeb.Gettext + alias LSGWeb.Router.Helpers, as: Routes end end @@ -65,6 +69,8 @@ defmodule LSGWeb do import LSGWeb.Gettext import Phoenix.LiveView.Helpers + + alias LSGWeb.Router.Helpers, as: Routes end end diff --git a/lib/lsg_web/context_plug.ex b/lib/lsg_web/context_plug.ex index 7896ace..29eab28 100644 --- a/lib/lsg_web/context_plug.ex +++ b/lib/lsg_web/context_plug.ex @@ -6,9 +6,16 @@ defmodule LSGWeb.ContextPlug do opts || [] end + def get_account(conn) do + cond do + get_session(conn, :account) -> get_session(conn, :account) + get_session(conn, :oidc_id) -> if account = IRC.Account.find_meta_account("identity-id", get_session(conn, :oidc_id)), do: account.id + end + end + def call(conn, opts) do account = with \ - {:account, account_id} when is_binary(account_id) <- {:account, get_session(conn, :account)}, + {:account, account_id} when is_binary(account_id) <- {:account, get_account(conn)}, {:account, account} when not is_nil(account) <- {:account, IRC.Account.get(account_id)} do account @@ -19,6 +26,8 @@ defmodule LSGWeb.ContextPlug do network = Map.get(conn.params, "network") network = if network == "-", do: nil, else: network + oidc_account = IRC.Account.find_meta_account("identity-id", get_session(conn, :oidc_id)) + conns = IRC.Connection.get_network(network) chan = if c = Map.get(conn.params, "chan") do LSGWeb.reformat_chan(c) @@ -74,6 +83,7 @@ defmodule LSGWeb.ContextPlug do |> assign(:chan, chan) |> assign(:bot, bot) |> assign(:account, account) + |> assign(:oidc_account, oidc_account) |> assign(:memberships, memberships) end end diff --git a/lib/lsg_web/controllers/irc_controller.ex b/lib/lsg_web/controllers/irc_controller.ex index 32007c2..d518481 100644 --- a/lib/lsg_web/controllers/irc_controller.ex +++ b/lib/lsg_web/controllers/irc_controller.ex @@ -5,7 +5,7 @@ defmodule LSGWeb.IrcController do def index(conn, params) do network = Map.get(params, "network") - channel = if c = Map.get(params, "channel"), do: LSGWeb.reformat_chan(c) + channel = if c = Map.get(params, "chan"), do: LSGWeb.reformat_chan(c) commands = for mod <- Enum.uniq([IRC.Account.AccountPlugin] ++ IRC.Plugin.enabled()) do if is_atom(mod) do identifier = Module.split(mod) |> List.last |> String.replace("Plugin", "") |> Macro.underscore @@ -15,8 +15,9 @@ defmodule LSGWeb.IrcController do |> Enum.filter(& &1) |> Enum.filter(fn({_, doc}) -> doc end) members = cond do - network -> IRC.Membership.expanded_members_or_friends(conn.assigns.account, network, channel) - true -> IRC.Membership.of_account(conn.assigns.account) + network && channel -> Enum.map(IRC.UserTrack.channel(network, channel), fn(tuple) -> IRC.UserTrack.User.from_tuple(tuple) end) + true -> + IRC.Membership.of_account(conn.assigns.account) end render conn, "index.html", network: network, commands: commands, channel: channel, members: members end diff --git a/lib/lsg_web/controllers/open_id_controller.ex b/lib/lsg_web/controllers/open_id_controller.ex new file mode 100644 index 0000000..d5af318 --- /dev/null +++ b/lib/lsg_web/controllers/open_id_controller.ex @@ -0,0 +1,64 @@ +defmodule LSGWeb.OpenIdController do + use LSGWeb, :controller + plug LSGWeb.ContextPlug, restrict: :public + require Logger + + def login(conn, _) do + url = OAuth2.Client.authorize_url!(new_client(), scope: "openid", state: Base.url_encode64(:crypto.strong_rand_bytes(32), padding: false)) + redirect(conn, external: url) + end + + def callback(conn, %{"error" => error_code, "error_description" => error}) do + Logger.warn("OpenId error: #{error_code} #{error}") + render(conn, "error.html", error: error) + end + + def callback(conn, %{"code" => code, "state" => state}) do + with \ + client = %{token: %OAuth2.AccessToken{access_token: json}} = OAuth2.Client.get_token!(new_client(), state: state, code: code), + {:ok, %{"access_token" => token}} <- Jason.decode(json), + client = %OAuth2.Client{client | token: %OAuth2.AccessToken{access_token: token}}, + {:ok, %OAuth2.Response{body: body}} <- OAuth2.Client.get(client, "/userinfo"), + {:ok, %{"sub" => id, "preferred_username" => username}} <- Jason.decode(body) + do + if account = conn.assigns.account do + if !IRC.Account.get_meta(account, "identity-id") do # XXX: And oidc id not linked yet + IRC.Account.put_meta(account, "identity-id", id) + end + IRC.Account.put_meta(account, "identity-username", username) + conn + else + conn + end + + conn + |> put_session(:oidc_id, id) + |> put_flash(:info, "Logged in!") + |> redirect(to: Routes.path(conn, "/")) + else + {:error, %OAuth2.Response{status_code: 401}} -> + Logger.error("OpenID: Unauthorized token") + render(conn, "error.html", error: "The token is invalid.") + {:error, %OAuth2.Error{reason: reason}} -> + Logger.error("Error: #{inspect reason}") + render(conn, "error.html", error: reason) + end + end + + def callback(conn, _params) do + render(conn, "error.html", error: "Unspecified error.") + end + + defp new_client() do + config = Application.get_env(:lsg, :oidc) + OAuth2.Client.new([ + strategy: OAuth2.Strategy.AuthCode, + client_id: config[:client_id], + client_secret: config[:client_secret], + site: config[:base_url], + authorize_url: config[:authorize_url], + token_url: config[:token_url], + redirect_uri: Routes.open_id_url(LSGWeb.Endpoint, :callback) + ]) + end +end diff --git a/lib/lsg_web/live/chat_live.ex b/lib/lsg_web/live/chat_live.ex index d736d72..e7a44db 100644 --- a/lib/lsg_web/live/chat_live.ex +++ b/lib/lsg_web/live/chat_live.ex @@ -22,7 +22,10 @@ defmodule LSGWeb.ChatLive do Map.put(acc, id, user) end) - {backlog, _} = LSG.IRC.BufferPlugin.select_buffer(connection.network, chan) + backlog = case LSG.IRC.BufferPlugin.select_buffer(connection.network, chan) do + {backlog, _} -> Enum.reverse(backlog) + _ -> [] + end socket = socket |> assign(:connection_id, connection.id) @@ -31,7 +34,7 @@ defmodule LSGWeb.ChatLive do |> assign(:title, "live") |> assign(:channel, chan) |> assign(:account_id, account.id) - |> assign(:backlog, Enum.reverse(backlog)) + |> assign(:backlog, backlog) |> assign(:users, users) |> assign(:counter, 0) diff --git a/lib/lsg_web/router.ex b/lib/lsg_web/router.ex index e872cef..eba71dc 100644 --- a/lib/lsg_web/router.ex +++ b/lib/lsg_web/router.ex @@ -32,10 +32,15 @@ defmodule LSGWeb.Router do get "/", PageController, :index get "/login/irc/:token", PageController, :token, as: :login + get "/login/oidc", OpenIdController, :login + get "/login/oidc/callback", OpenIdController, :callback + get "/api/untappd/callback", UntappdController, :callback, as: :untappd_callback + get "/-", IrcController, :index get "/-/txt", IrcController, :txt get "/-/txt/:name", IrcController, :txt + get "/-/alcoolog", AlcoologController, :index get "/-/alcoolog/~/:account_name", AlcoologController, :index get "/:network", NetworkController, :index @@ -48,6 +53,7 @@ defmodule LSGWeb.Router do get "/:network/:chan/alcoolog", AlcoologController, :index get "/:network/:chan/alcoolog/gls.json", AlcoologController, :index_gls_json put "/api/alcoolog/minisync/:user_id/meta/:key", AlcoologController, :minisync_put_meta + get "/:network/:chan", IrcController, :index live "/:network/:chan/live", ChatLive get "/:network/:chan/txt", IrcController, :txt diff --git a/lib/lsg_web/templates/alcoolog/auth.html.eex b/lib/lsg_web/templates/alcoolog/auth.html.eex index af6db53..6e5cedc 100644 --- a/lib/lsg_web/templates/alcoolog/auth.html.eex +++ b/lib/lsg_web/templates/alcoolog/auth.html.eex @@ -1,6 +1,11 @@ -<h1>authentication</h1> +<div class="grid grid-cols-2"> + <h1 class="text-2xl font-bold">authentication</h1> + <div class="text-right"> + <%= link("connect using random.sh", to: "/login/oidc", class: "inline-block font-medium underline") %> + </div> +</div> -<div id="authenticator"> +<div id="authenticator" class="mt-12 h-32 place-self-end justify-self-start"> <p> <%= if @bot, do: "Send this to #{@bot} on #{@network}:", else: "Find your bot nickname and send:" %><br /><br /> <strong>/msg <%= @bot || "the-bot-nickname" %> web</strong> @@ -12,7 +17,7 @@ <script type="text/javascript"> var authSse = new EventSource("/api/irc-auth.sse?redirect_to=" + window.location.pathname); authSse.addEventListener("token", function(event) { - html = "<%= if @bot, do: "Send this to #{@bot} on #{@network}:", else: "Find your bot nickname and send:" %><br /><br /><strong>/msg <%= @bot || "the-bot-nickname" %> "+event.data+"</strong>"; + html = "<%= if @bot, do: "Send this to #{@bot} on #{@network}:", else: "Find your bot nickname and send:" %><br /><br /><strong class='text-xl font-mono ml-12'>/msg <%= @bot || "<the-bot-nickname>" %> "+event.data+"</strong>"; elem = document.getElementById("authenticator"); elem.innerHTML = html; }); @@ -36,4 +41,3 @@ }; </script> - diff --git a/lib/lsg_web/templates/irc/index.html.eex b/lib/lsg_web/templates/irc/index.html.eex index f20f444..52372b5 100644 --- a/lib/lsg_web/templates/irc/index.html.eex +++ b/lib/lsg_web/templates/irc/index.html.eex @@ -1,22 +1,21 @@ <div class="hidden sm:block"> - <nav class="flex flex-wrap content-center"> - <%= link("live", to: LSGWeb.Router.Helpers.live_path(LSGWeb.Endpoint, LSGWeb.ChatLive, @network, LSGWeb.format_chan(@chan)), class: "px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-indigo-600 focus:bg-indigo-50") %> + <nav class="flex flex-wrap space-x-4"> + <%= link("live", to: LSGWeb.Router.Helpers.live_path(LSGWeb.Endpoint, LSGWeb.ChatLive, @network, LSGWeb.format_chan(@chan)), class: "py-4 font-medium leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-indigo-600 focus:bg-indigo-50", 'data-turbo': false) %> <% list = for {identifier, _} <- @commands do %> <% name = String.replace(identifier, "_", " ") %> - <%= link(name, to: "##{identifier}", class: "px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-indigo-600 focus:bg-indigo-50") %> + <%= link(name, to: "##{identifier}", class: "py-4 font-medium leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-indigo-600 focus:bg-indigo-50") %> <% end %> <%= list %> </nav> </div> <%= if @members != [] do %> -<% users = for {_acc, user, nick} <- @members, do: if(user, do: nick, else: content_tag(:i, nick)) %> -<%= for u <- Enum.intersperse(users, ", ") do %> - <%= u %> -<% end %> + <ul class="flex flew-wrap space-x-4 mt-12"> + <%= for user <- @members do %><li><%= user.nick %></li><% end %> + </ul> <% end %> -<div class="irchelps space-y-6"> +<div class="irchelps space-y-6 mt-12"> <%= for {identifier, help} <- @commands do %> <%= if help do %> <div class="bg-white border-b border-gray-200" id="<%= identifier %>"> diff --git a/lib/lsg_web/templates/layout/app.html.eex b/lib/lsg_web/templates/layout/app.html.eex index bca1555..b918ca5 100644 --- a/lib/lsg_web/templates/layout/app.html.eex +++ b/lib/lsg_web/templates/layout/app.html.eex @@ -107,8 +107,8 @@ <h1 class="text-3xl leading-9 font-bold text-white"> <%= if n = @conn.assigns[:network] do %><a href="/<%= n %>"><%= n %></a> › <% end %> <%= if c = @conn.assigns[:chan] do %><a href="/<%= @conn.assigns.network %>/<%= LSGWeb.format_chan(c) %>"><%= c %></a> › <% end %> - <%= for({name, href} <- @conn.assigns[:breadcrumbs]||[], do: [link(name, to: href), raw(" › ")]) %> - <%= @conn.assigns[:title] || @conn.assigns[:chan] || @conn.assigns[:network] %> + <%= for({name, href} <- Enum.uniq(@conn.assigns[:breadcrumbs]||[]), do: [link(name, to: href), raw(" › ")]) %> + <%= @conn.assigns[:title] %> </h1> </div> </header> diff --git a/lib/lsg_web/templates/open_id/error.html.eex b/lib/lsg_web/templates/open_id/error.html.eex new file mode 100644 index 0000000..d1b35b9 --- /dev/null +++ b/lib/lsg_web/templates/open_id/error.html.eex @@ -0,0 +1,3 @@ +<h1 class="text-xl font-bold text-red-800">OpenID authentication error</h1> + +<p class="mt-12 text-base prose"><%= @error %></p> diff --git a/lib/lsg_web/templates/page/user.html.eex b/lib/lsg_web/templates/page/user.html.eex index 56fc485..6eeb39d 100644 --- a/lib/lsg_web/templates/page/user.html.eex +++ b/lib/lsg_web/templates/page/user.html.eex @@ -1,6 +1,9 @@ <div class="prose prose-lg"> <ul> - <li><%= link("Help", to: "/-") %></li> + <li><%= link("Help", to: "/-") %></li> + <%= unless List.keyfind(@metas, "identity-id", 0) do %> + <li><%= link("Connect with random.sh", to: Routes.open_id_path(@conn, :login)) %></li> + <% end %> </ul> <h2>channels</h2> diff --git a/lib/lsg_web/views/open_id_view.ex b/lib/lsg_web/views/open_id_view.ex new file mode 100644 index 0000000..64d4430 --- /dev/null +++ b/lib/lsg_web/views/open_id_view.ex @@ -0,0 +1,4 @@ +defmodule LSGWeb.OpenIdView do + use LSGWeb, :view + +end |