From cf7b21afa87ca5202bd626d26a60adab1e9131a1 Mon Sep 17 00:00:00 2001 From: href Date: Tue, 7 Sep 2021 05:28:02 +0200 Subject: web: openid & various fixes --- lib/lsg_web/context_plug.ex | 12 ++++- lib/lsg_web/controllers/irc_controller.ex | 7 +-- lib/lsg_web/controllers/open_id_controller.ex | 64 +++++++++++++++++++++++++++ lib/lsg_web/live/chat_live.ex | 7 ++- lib/lsg_web/router.ex | 6 +++ lib/lsg_web/templates/alcoolog/auth.html.eex | 12 +++-- lib/lsg_web/templates/irc/index.html.eex | 15 +++---- lib/lsg_web/templates/layout/app.html.eex | 4 +- lib/lsg_web/templates/open_id/error.html.eex | 3 ++ lib/lsg_web/templates/page/user.html.eex | 5 ++- lib/lsg_web/views/open_id_view.ex | 4 ++ 11 files changed, 118 insertions(+), 21 deletions(-) create mode 100644 lib/lsg_web/controllers/open_id_controller.ex create mode 100644 lib/lsg_web/templates/open_id/error.html.eex create mode 100644 lib/lsg_web/views/open_id_view.ex (limited to 'lib/lsg_web') 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 @@ -

authentication

+
+

authentication

+
+ <%= link("connect using random.sh", to: "/login/oidc", class: "inline-block font-medium underline") %> +
+
-
+

<%= if @bot, do: "Send this to #{@bot} on #{@network}:", else: "Find your bot nickname and send:" %>

/msg <%= @bot || "the-bot-nickname" %> web @@ -12,7 +17,7 @@ - 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 @@

<%= 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 %> +
    + <%= for user <- @members do %>
  • <%= user.nick %>
  • <% end %> +
<% end %> -
+
<%= for {identifier, help} <- @commands do %> <%= if help do %>
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 @@

<%= if n = @conn.assigns[:network] do %><%= n %> › <% end %> <%= if c = @conn.assigns[:chan] do %><%= c %> › <% 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] %>

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 @@ +

OpenID authentication error

+ +

<%= @error %>

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 @@
    -
  • <%= link("Help", to: "/-") %>
  • +
  • <%= link("Help", to: "/-") %>
  • + <%= unless List.keyfind(@metas, "identity-id", 0) do %> +
  • <%= link("Connect with random.sh", to: Routes.open_id_path(@conn, :login)) %>
  • + <% end %>

channels

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 -- cgit v1.2.3