Remove openid support
I switched to an external solution that supports more protocols.
This commit is contained in:
parent
88f5c52c50
commit
cd6a6cb77c
25 changed files with 10 additions and 1029 deletions
|
@ -109,7 +109,7 @@ if config_env() == :prod do
|
|||
relay: System.get_env("SMTP_RELAY"),
|
||||
username: System.get_env("SMTP_USERNAME"),
|
||||
password: System.get_env("SMTP_PASSWORD"),
|
||||
ssl: true,
|
||||
ssl: System.get_env("SMTP_SSL") == "true",
|
||||
tls: :always,
|
||||
auth: :always,
|
||||
port: Integer.parse(System.get_env("SMTP_PORT") || "465")
|
||||
|
|
|
@ -1,171 +0,0 @@
|
|||
defmodule Comfycamp.SSO do
|
||||
@moduledoc """
|
||||
The SSO context.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias Comfycamp.Repo
|
||||
|
||||
alias Comfycamp.SSO.OIDCApp
|
||||
alias Comfycamp.SSO.OIDCCode
|
||||
alias Comfycamp.SSO.OIDCRedirectURI
|
||||
|
||||
@doc """
|
||||
Returns the list of oidc_apps.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_oidc_apps()
|
||||
[%OIDCApp{}, ...]
|
||||
|
||||
"""
|
||||
def list_oidc_apps do
|
||||
Repo.all(OIDCApp)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single oidc_app.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the Oidc app does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_oidc_app!(123)
|
||||
%OIDCApp{}
|
||||
|
||||
iex> get_oidc_app!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_oidc_app!(id) do
|
||||
query =
|
||||
from a in OIDCApp,
|
||||
preload: [:redirect_uris],
|
||||
where: a.client_id == ^id
|
||||
|
||||
Repo.one!(query)
|
||||
end
|
||||
|
||||
def get_oidc_app_by_secret!(client_secret) do
|
||||
query =
|
||||
from a in OIDCApp,
|
||||
where: a.client_secret == ^client_secret
|
||||
|
||||
Repo.one!(query)
|
||||
end
|
||||
|
||||
def has_redirect_uri?(client_id, redirect_uri) do
|
||||
query =
|
||||
from a in OIDCApp,
|
||||
join: u in assoc(a, :redirect_uris),
|
||||
where: u.uri == ^redirect_uri and a.client_id == ^client_id
|
||||
|
||||
Repo.aggregate(query, :count) >= 1
|
||||
end
|
||||
|
||||
def get_oidc_redirect_uri!(id), do: Repo.get(OIDCRedirectURI, id)
|
||||
|
||||
def get_oidc_code!(value) do
|
||||
ten_minutes_ago = DateTime.utc_now() |> DateTime.add(-600, :second)
|
||||
|
||||
query =
|
||||
from c in OIDCCode,
|
||||
preload: [:oidc_app, :user],
|
||||
where: c.value == ^value and c.inserted_at >= ^ten_minutes_ago
|
||||
|
||||
Repo.one!(query)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a oidc_app.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> create_oidc_app(%{field: value})
|
||||
{:ok, %OIDCApp{}}
|
||||
|
||||
iex> create_oidc_app(%{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_oidc_app(attrs \\ %{}) do
|
||||
%OIDCApp{}
|
||||
|> OIDCApp.creation_changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Create a temporary code for OIDC app
|
||||
that may be exchanged for an access token.
|
||||
"""
|
||||
def create_oidc_code(attrs \\ %{}) do
|
||||
%OIDCCode{}
|
||||
|> OIDCCode.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def create_oidc_redirect_uri(attrs \\ %{}) do
|
||||
%OIDCRedirectURI{}
|
||||
|> OIDCRedirectURI.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a oidc_app.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_oidc_app(oidc_app, %{field: new_value})
|
||||
{:ok, %OIDCApp{}}
|
||||
|
||||
iex> update_oidc_app(oidc_app, %{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def update_oidc_app(%OIDCApp{} = oidc_app, attrs) do
|
||||
oidc_app
|
||||
|> OIDCApp.update_changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a oidc_app.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> delete_oidc_app(oidc_app)
|
||||
{:ok, %OIDCApp{}}
|
||||
|
||||
iex> delete_oidc_app(oidc_app)
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def delete_oidc_app(%OIDCApp{} = oidc_app) do
|
||||
Repo.delete(oidc_app)
|
||||
end
|
||||
|
||||
def delete_oidc_code(%OIDCCode{} = code) do
|
||||
Repo.delete(code)
|
||||
end
|
||||
|
||||
def delete_oidc_redirect_uri(%OIDCRedirectURI{} = uri) do
|
||||
Repo.delete(uri)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking oidc_app changes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> change_oidc_app(oidc_app)
|
||||
%Ecto.Changeset{data: %OIDCApp{}}
|
||||
|
||||
"""
|
||||
def change_oidc_app(%OIDCApp{} = oidc_app, attrs \\ %{}) do
|
||||
OIDCApp.update_changeset(oidc_app, attrs)
|
||||
end
|
||||
|
||||
def change_oidc_redirect_uri(%OIDCRedirectURI{} = oidc_redirect_uri, attrs \\ %{}) do
|
||||
OIDCRedirectURI.changeset(oidc_redirect_uri, attrs)
|
||||
end
|
||||
end
|
|
@ -1,25 +0,0 @@
|
|||
defmodule Comfycamp.SSO.IDToken do
|
||||
@derive Jason.Encoder
|
||||
defstruct [:iss, :sub, :aud, :exp, :iat, :email, :preferred_username, :nonce]
|
||||
|
||||
def build_id_token(user, client_id, nonce) do
|
||||
{_, now} = DateTime.now("Etc/UTC")
|
||||
issued_at = DateTime.to_unix(now)
|
||||
|
||||
expires_at =
|
||||
now
|
||||
|> DateTime.add(1, :day)
|
||||
|> DateTime.to_unix()
|
||||
|
||||
%Comfycamp.SSO.IDToken{
|
||||
iss: "https://" <> System.get_env("PHX_HOST"),
|
||||
sub: Integer.to_string(user.id),
|
||||
aud: client_id,
|
||||
exp: expires_at,
|
||||
iat: issued_at,
|
||||
email: user.email,
|
||||
preferred_username: user.username,
|
||||
nonce: nonce
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,36 +0,0 @@
|
|||
defmodule Comfycamp.SSO.OIDCApp do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Comfycamp.SSO.OIDCCode
|
||||
alias Comfycamp.SSO.OIDCRedirectURI
|
||||
alias Comfycamp.Rand
|
||||
|
||||
@derive {Phoenix.Param, key: :client_id}
|
||||
@primary_key {:client_id, :string, autogenerate: false}
|
||||
schema "oidc_apps" do
|
||||
field :client_secret, :string
|
||||
field :name, :string
|
||||
field :enabled, :boolean, default: false
|
||||
|
||||
has_many :codes, OIDCCode, foreign_key: :oidc_app_id
|
||||
has_many :redirect_uris, OIDCRedirectURI, foreign_key: :oidc_app_id
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def update_changeset(oidc_app, attrs) do
|
||||
oidc_app
|
||||
|> cast(attrs, [:name, :enabled])
|
||||
|> validate_required([:name, :enabled])
|
||||
|> validate_length(:name, min: 2, max: 48)
|
||||
end
|
||||
|
||||
def creation_changeset(oidc_app, attrs) do
|
||||
oidc_app
|
||||
|> update_changeset(attrs)
|
||||
|> change(client_id: Rand.get_random_string(20))
|
||||
|> change(client_secret: Rand.get_random_string(32))
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
defmodule Comfycamp.SSO.OIDCCode do
|
||||
@moduledoc """
|
||||
Temporary code that may be exchanged for an access token.
|
||||
"""
|
||||
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Comfycamp.Accounts.User
|
||||
alias Comfycamp.SSO.OIDCApp
|
||||
alias Comfycamp.Rand
|
||||
|
||||
@derive {Phoenix.Param, key: :value}
|
||||
@primary_key {:value, :string, autogenerate: false}
|
||||
schema "oidc_codes" do
|
||||
field :redirect_uri, :string
|
||||
field :nonce, :string
|
||||
belongs_to :user, User
|
||||
|
||||
belongs_to :oidc_app, OIDCApp,
|
||||
type: :string,
|
||||
foreign_key: :oidc_app_id,
|
||||
references: :client_id
|
||||
|
||||
timestamps(type: :utc_datetime, updated_at: false)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(oidc_code, attrs) do
|
||||
oidc_code
|
||||
|> change(value: Rand.get_random_string(12))
|
||||
|> cast(attrs, [:user_id, :oidc_app_id, :redirect_uri, :nonce])
|
||||
|> validate_required([:user_id, :oidc_app_id, :redirect_uri])
|
||||
end
|
||||
end
|
|
@ -1,22 +0,0 @@
|
|||
defmodule Comfycamp.SSO.OIDCRedirectURI do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Comfycamp.SSO.OIDCApp
|
||||
|
||||
schema "oidc_redirect_uris" do
|
||||
field :uri, :string
|
||||
|
||||
belongs_to :oidc_app, OIDCApp,
|
||||
type: :string,
|
||||
foreign_key: :oidc_app_id,
|
||||
references: :client_id
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(uri, attrs) do
|
||||
uri
|
||||
|> cast(attrs, [:uri, :oidc_app_id])
|
||||
|> validate_required([:uri, :oidc_app_id])
|
||||
end
|
||||
end
|
|
@ -21,11 +21,6 @@
|
|||
Пользователи
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link href={~p"/admin/oidc_apps"}>
|
||||
OpenID
|
||||
</.link>
|
||||
</li>
|
||||
<li>
|
||||
<.link href={~p"/admin/email"}>
|
||||
Email
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
defmodule ComfycampWeb.OauthController do
|
||||
use ComfycampWeb, :controller
|
||||
|
||||
alias Comfycamp.Accounts
|
||||
alias Comfycamp.SSO
|
||||
alias Comfycamp.SSO.OIDCApp
|
||||
alias Comfycamp.SSO.IDToken
|
||||
alias Comfycamp.Token
|
||||
|
||||
@doc """
|
||||
Check the request parameters and current user status,
|
||||
then ask the user to confirm that he wants to share his info with Relying Party.
|
||||
"""
|
||||
def authorize(conn, %{"client_id" => client_id, "redirect_uri" => redirect_uri} = params) do
|
||||
app = SSO.get_oidc_app!(client_id)
|
||||
current_user = conn.assigns.current_user
|
||||
|
||||
with {:is_approved, true} <- {:is_approved, current_user.is_approved},
|
||||
{:response_type, "code"} <- {:response_type, params["response_type"]},
|
||||
{:is_enabled, true} <- {:is_enabled, app.enabled},
|
||||
{:has_uri, true} <- {:has_uri, SSO.has_redirect_uri?(client_id, redirect_uri)} do
|
||||
render(conn, :authorize,
|
||||
page_title: "Подтвердите вход",
|
||||
app_name: app.name,
|
||||
params: URI.encode_query(params)
|
||||
)
|
||||
else
|
||||
{:is_approved, false} ->
|
||||
render(conn, :error,
|
||||
description:
|
||||
"Ваш аккаунт ещё не был одобрен, подождите немного или свяжитесь с администратором"
|
||||
)
|
||||
|
||||
{:response_type, response_type} ->
|
||||
render(conn, :error, description: "Неподдерживаемый response type: #{response_type}")
|
||||
|
||||
{:is_enabled, false} ->
|
||||
render(conn, :error, description: "Приложение отключено")
|
||||
|
||||
{:has_uri, false} ->
|
||||
render(conn, :error, description: "Redirect URI не зарегистрирован или отсутствует")
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generate an Authorization Code and redirect the user back to Relying Party.
|
||||
"""
|
||||
def generate_code(conn, %{"client_id" => client_id, "redirect_uri" => redirect_uri} = params) do
|
||||
app = SSO.get_oidc_app!(client_id)
|
||||
current_user = conn.assigns.current_user
|
||||
|
||||
with {:is_approved, true} <- {:is_approved, current_user.is_approved},
|
||||
{:is_enabled, true} <- {:is_enabled, app.enabled},
|
||||
{:has_uri, true} <- {:has_uri, SSO.has_redirect_uri?(client_id, redirect_uri)} do
|
||||
{:ok, code} =
|
||||
SSO.create_oidc_code(%{
|
||||
oidc_app_id: client_id,
|
||||
user_id: conn.assigns.current_user.id,
|
||||
redirect_uri: redirect_uri,
|
||||
nonce: params["nonce"]
|
||||
})
|
||||
|
||||
uri = build_redirect_uri(redirect_uri, code.value, params["state"])
|
||||
redirect(conn, external: uri)
|
||||
else
|
||||
{:is_approved, false} ->
|
||||
render(conn, :error,
|
||||
description:
|
||||
"Ваш аккаунт ещё не был одобрен, подождите немного или свяжитесь с администратором"
|
||||
)
|
||||
|
||||
{:is_enabled, false} ->
|
||||
render(conn, :error, description: "Приложение отключено")
|
||||
|
||||
{:has_uri, false} ->
|
||||
render(conn, :error, description: "Redirect URI не зарегистрирован или отсутствует")
|
||||
end
|
||||
end
|
||||
|
||||
def token(conn, params = %{"code" => code_value, "redirect_uri" => redirect_uri}) do
|
||||
with {:client_info, {:ok, client_id, client_secret}} <-
|
||||
{:client_info, get_client_info(conn, params)},
|
||||
{:code, code} <- {:code, SSO.get_oidc_code!(code_value)},
|
||||
{:uri, ^redirect_uri} <- {:uri, code.redirect_uri},
|
||||
{:app, oidc_app = %OIDCApp{enabled: true, client_id: ^client_id}} <-
|
||||
{:app, SSO.get_oidc_app_by_secret!(client_secret)},
|
||||
{:code_ref, ^client_id} <- {:code_ref, code.oidc_app.client_id} do
|
||||
SSO.delete_oidc_code(code)
|
||||
|
||||
{access_token, refresh_token} = Accounts.generate_oauth_tokens(code.user)
|
||||
|
||||
id_token = IDToken.build_id_token(code.user, oidc_app.client_id, code.nonce)
|
||||
{:ok, signed_id_token} = Token.sign(id_token, client_secret)
|
||||
|
||||
render(conn, :token,
|
||||
access_token: Base.url_encode64(access_token),
|
||||
refresh_token: Base.url_encode64(refresh_token),
|
||||
id_token: signed_id_token
|
||||
)
|
||||
else
|
||||
{:client_info, _} ->
|
||||
render(conn, :error, description: "Нет client id или client secret")
|
||||
|
||||
{:code, _} ->
|
||||
render(conn, :error, description: "Не удалось найти временный код")
|
||||
|
||||
{:uri, _} ->
|
||||
render(conn, :error, description: "Redirect URI не совпадает с изначальным значением")
|
||||
|
||||
{:app, _} ->
|
||||
render(conn, :error, description: "Приложение не найдено или отключено")
|
||||
|
||||
{:code_ref, _} ->
|
||||
render(conn, :error, description: "Временный код выдан для другого приложения")
|
||||
end
|
||||
end
|
||||
|
||||
def openid_discovery(conn, _params) do
|
||||
render(conn, :openid_discovery)
|
||||
end
|
||||
|
||||
def user_info(conn, _params) do
|
||||
render(conn, :user_info, user: conn.assigns.oauth_user)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Extract client id and client secret from request parameters or headers.
|
||||
Returns {:ok, "client_id", "client_secret"} on success.
|
||||
"""
|
||||
def get_client_info(_conn, %{"client_id" => client_id, "client_secret" => client_secret}) do
|
||||
{:ok, client_id, client_secret}
|
||||
end
|
||||
|
||||
def get_client_info(conn, _params) do
|
||||
with [header] <- Plug.Conn.get_req_header(conn, "authorization"),
|
||||
"Basic " <> b64 <- header,
|
||||
{:ok, keys} <- Base.decode64(b64),
|
||||
[client_id, client_secret] <- String.split(keys, ":") do
|
||||
{:ok, client_id, client_secret}
|
||||
else
|
||||
_ -> {:error, "Invalid Authorization header"}
|
||||
end
|
||||
end
|
||||
|
||||
defp build_redirect_uri(redirect_uri, code, state) do
|
||||
parsed_uri = URI.parse(redirect_uri)
|
||||
|
||||
query =
|
||||
build_query_params(code, state)
|
||||
|> URI.encode_query()
|
||||
|
||||
%{parsed_uri | query: query}
|
||||
|> URI.to_string()
|
||||
end
|
||||
|
||||
defp build_query_params(code, state) do
|
||||
params = %{"code" => code}
|
||||
|
||||
if state do
|
||||
Map.put(params, "state", state)
|
||||
else
|
||||
params
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
defmodule ComfycampWeb.OauthHTML do
|
||||
use ComfycampWeb, :html
|
||||
|
||||
def authorize(assigns) do
|
||||
~H"""
|
||||
<h1>Подтвердите вход</h1>
|
||||
<p>Приложению "<%= @app_name %>" будут доступны:</p>
|
||||
<ul>
|
||||
<li>Логин</li>
|
||||
<li>Email</li>
|
||||
</ul>
|
||||
|
||||
<.link href={"/oauth/generate_code?#{@params}"} method="POST">Разрешить доступ</.link>
|
||||
"""
|
||||
end
|
||||
|
||||
def error(assigns) do
|
||||
~H"""
|
||||
<h1>Ошибка</h1>
|
||||
<p><%= @description %></p>
|
||||
"""
|
||||
end
|
||||
end
|
|
@ -1,39 +0,0 @@
|
|||
defmodule ComfycampWeb.OauthJSON do
|
||||
def token(%{access_token: access_token, refresh_token: refresh_token, id_token: id_token}) do
|
||||
%{
|
||||
access_token: access_token,
|
||||
token_type: "Bearer",
|
||||
refresh_token: refresh_token,
|
||||
id_token: id_token
|
||||
}
|
||||
end
|
||||
|
||||
def openid_discovery(_assigns) do
|
||||
%{
|
||||
issuer: "https://comfycamp.space",
|
||||
authorization_endpoint: "https://comfycamp.space/oauth/authorize",
|
||||
token_endpoint: "https://comfycamp.space/oauth/token",
|
||||
userinfo_endpoint: "https://comfycamp.space/oauth/userinfo",
|
||||
jwks_uri: "https://comfycamp.space/.well-known/jwks.json",
|
||||
subject_types_supported: ["public"],
|
||||
response_types_supported: ["code"],
|
||||
id_token_signing_alg_values_supported: ["HS256"],
|
||||
scopes_supported: ["openid", "profile", "email"],
|
||||
claims_supported: ["sub", "email", "preferred_username"]
|
||||
}
|
||||
end
|
||||
|
||||
def user_info(%{user: user}) do
|
||||
%{
|
||||
sub: Integer.to_string(user.id),
|
||||
email: user.email,
|
||||
preferred_username: user.username
|
||||
}
|
||||
end
|
||||
|
||||
def error(assigns) do
|
||||
%{
|
||||
description: assigns["description"]
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,76 +0,0 @@
|
|||
defmodule ComfycampWeb.OIDCAppController do
|
||||
use ComfycampWeb, :controller
|
||||
|
||||
alias Comfycamp.SSO
|
||||
alias Comfycamp.SSO.OIDCApp
|
||||
|
||||
def index(conn, _params) do
|
||||
oidc_apps = SSO.list_oidc_apps()
|
||||
|
||||
conn
|
||||
|> put_layout(html: :admin)
|
||||
|> render(:index, oidc_apps: oidc_apps)
|
||||
end
|
||||
|
||||
def new(conn, _params) do
|
||||
changeset = SSO.change_oidc_app(%OIDCApp{})
|
||||
|
||||
conn
|
||||
|> put_layout(html: :admin)
|
||||
|> render(:new, changeset: changeset)
|
||||
end
|
||||
|
||||
def create(conn, %{"oidc_app" => oidc_app_params}) do
|
||||
case SSO.create_oidc_app(oidc_app_params) do
|
||||
{:ok, oidc_app} ->
|
||||
conn
|
||||
|> put_flash(:info, "Oidc app created successfully.")
|
||||
|> redirect(to: ~p"/admin/oidc_apps/#{oidc_app}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
conn
|
||||
|> put_layout(html: :admin)
|
||||
|> render(:new, changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
def show(conn, %{"id" => client_id}) do
|
||||
oidc_app = SSO.get_oidc_app!(client_id)
|
||||
|
||||
conn
|
||||
|> put_layout(html: :admin)
|
||||
|> render(:show, oidc_app: oidc_app)
|
||||
end
|
||||
|
||||
def edit(conn, %{"id" => client_id}) do
|
||||
oidc_app = SSO.get_oidc_app!(client_id)
|
||||
changeset = SSO.change_oidc_app(oidc_app)
|
||||
|
||||
conn
|
||||
|> put_layout(html: :admin)
|
||||
|> render(:edit, oidc_app: oidc_app, changeset: changeset)
|
||||
end
|
||||
|
||||
def update(conn, %{"id" => client_id, "oidc_app" => oidc_app_params}) do
|
||||
oidc_app = SSO.get_oidc_app!(client_id)
|
||||
|
||||
case SSO.update_oidc_app(oidc_app, oidc_app_params) do
|
||||
{:ok, oidc_app} ->
|
||||
conn
|
||||
|> put_flash(:info, "Oidc app updated successfully.")
|
||||
|> redirect(to: ~p"/admin/oidc_apps/#{oidc_app}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, :edit, oidc_app: oidc_app, changeset: changeset)
|
||||
end
|
||||
end
|
||||
|
||||
def delete(conn, %{"id" => client_id}) do
|
||||
oidc_app = SSO.get_oidc_app!(client_id)
|
||||
{:ok, _oidc_app} = SSO.delete_oidc_app(oidc_app)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "Oidc app deleted successfully.")
|
||||
|> redirect(to: ~p"/admin/oidc_apps")
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
defmodule ComfycampWeb.OIDCAppHTML do
|
||||
use ComfycampWeb, :html
|
||||
|
||||
embed_templates "oidc_app_html/*"
|
||||
|
||||
@doc """
|
||||
Renders a oidc_app form.
|
||||
"""
|
||||
attr :changeset, Ecto.Changeset, required: true
|
||||
attr :action, :string, required: true
|
||||
|
||||
def oidc_app_form(assigns)
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
<div>
|
||||
<.header>
|
||||
Edit OpenID app "<%= @oidc_app.name %>"
|
||||
<:subtitle>Use this form to manage oidc_app records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.back navigate={~p"/admin/oidc_apps"}>Back to OpenID apps</.back>
|
||||
|
||||
<.oidc_app_form changeset={@changeset} action={~p"/admin/oidc_apps/#{@oidc_app}"} />
|
||||
</div>
|
|
@ -1,28 +0,0 @@
|
|||
<div>
|
||||
<.header>
|
||||
Listing OpenID Connect apps
|
||||
<:actions>
|
||||
<.link href={~p"/admin/oidc_apps/new"}>
|
||||
<.button>New OpenID Connect app</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.table id="oidc_apps" rows={@oidc_apps} row_click={&JS.navigate(~p"/admin/oidc_apps/#{&1}")}>
|
||||
<:col :let={oidc_app} label="Name"><%= oidc_app.name %></:col>
|
||||
<:col :let={oidc_app} label="Client"><%= oidc_app.client_id %></:col>
|
||||
<:col :let={oidc_app} label="Client secret"><%= oidc_app.client_secret %></:col>
|
||||
<:col :let={oidc_app} label="Enabled"><%= oidc_app.enabled %></:col>
|
||||
<:action :let={oidc_app}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/admin/oidc_apps/#{oidc_app}"}>Show</.link>
|
||||
</div>
|
||||
<.link navigate={~p"/admin/oidc_apps/#{oidc_app}/edit"}>Edit</.link>
|
||||
</:action>
|
||||
<:action :let={oidc_app}>
|
||||
<.link href={~p"/admin/oidc_apps/#{oidc_app}"} method="delete" data-confirm="Are you sure?">
|
||||
Delete
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
</div>
|
|
@ -1,10 +0,0 @@
|
|||
<div>
|
||||
<.header>
|
||||
New OpenID Connect app
|
||||
<:subtitle>Use this form to manage oidc_app records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.back navigate={~p"/admin/oidc_apps"}>Back to OpenID apps</.back>
|
||||
|
||||
<.oidc_app_form changeset={@changeset} action={~p"/admin/oidc_apps"} />
|
||||
</div>
|
|
@ -1,12 +0,0 @@
|
|||
<div>
|
||||
<.simple_form :let={f} for={@changeset} action={@action}>
|
||||
<.error :if={@changeset.action}>
|
||||
Oops, something went wrong! Please check the errors below.
|
||||
</.error>
|
||||
<.input field={f[:name]} type="text" label="Name" />
|
||||
<.input field={f[:enabled]} type="checkbox" label="Enabled" />
|
||||
<:actions>
|
||||
<.button>Save Oidc app</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
||||
</div>
|
|
@ -1,41 +0,0 @@
|
|||
<div>
|
||||
<.header>
|
||||
OpenID app "<%= @oidc_app.name %>"
|
||||
<:subtitle>This is a oidc_app record from your database.</:subtitle>
|
||||
<:actions>
|
||||
<.link href={~p"/admin/oidc_apps/#{@oidc_app}/edit"}>
|
||||
<.button>Edit OpenID app</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.list>
|
||||
<:item title="Name"><%= @oidc_app.name %></:item>
|
||||
<:item title="Client ID"><%= @oidc_app.client_id %></:item>
|
||||
<:item title="Client secret"><%= @oidc_app.client_secret %></:item>
|
||||
<:item title="Enabled"><%= @oidc_app.enabled %></:item>
|
||||
</.list>
|
||||
|
||||
<ul>
|
||||
<%= for uri <- @oidc_app.redirect_uris do %>
|
||||
<li>
|
||||
<%= uri.uri %>
|
||||
<.link
|
||||
href={~p"/admin/oidc_apps/#{@oidc_app}/redirect_uris/#{uri}"}
|
||||
method="delete"
|
||||
data-confirm="Хотите удалить ссылку?"
|
||||
>
|
||||
Удалить
|
||||
</.link>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<.link navigate={~p"/admin/oidc_apps/#{@oidc_app}/redirect_uris/new"}>
|
||||
<.button>
|
||||
Добавить redirect URI
|
||||
</.button>
|
||||
</.link>
|
||||
|
||||
<.back navigate={~p"/admin/oidc_apps"}>Back to OpenID apps</.back>
|
||||
</div>
|
|
@ -1,39 +0,0 @@
|
|||
defmodule ComfycampWeb.OIDCRedirectURIController do
|
||||
use ComfycampWeb, :controller
|
||||
|
||||
alias Comfycamp.SSO
|
||||
alias Comfycamp.SSO.OIDCRedirectURI
|
||||
|
||||
def new(conn, %{"client_id" => client_id}) do
|
||||
changeset = SSO.change_oidc_redirect_uri(%OIDCRedirectURI{})
|
||||
oidc_app = SSO.get_oidc_app!(client_id)
|
||||
|
||||
conn
|
||||
|> put_layout(html: :admin)
|
||||
|> render(:new, changeset: changeset, oidc_app: oidc_app)
|
||||
end
|
||||
|
||||
def create(conn, %{"client_id" => client_id, "oidc_redirect_uri" => uri_params}) do
|
||||
oidc_app = SSO.get_oidc_app!(client_id)
|
||||
uri_params = Map.put(uri_params, "oidc_app_id", client_id)
|
||||
|
||||
case SSO.create_oidc_redirect_uri(uri_params) do
|
||||
{:ok, _uri} ->
|
||||
conn
|
||||
|> redirect(to: ~p"/admin/oidc_apps/#{client_id}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
conn
|
||||
|> put_layout(html: :admin)
|
||||
|> render(:new, changeset: changeset, oidc_app: oidc_app)
|
||||
end
|
||||
end
|
||||
|
||||
def delete(conn, %{"client_id" => client_id, "id" => uri_id}) do
|
||||
uri = SSO.get_oidc_redirect_uri!(uri_id)
|
||||
{:ok, _uri} = SSO.delete_oidc_redirect_uri(uri)
|
||||
|
||||
conn
|
||||
|> redirect(to: ~p"/admin/oidc_apps/#{client_id}")
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
defmodule ComfycampWeb.OIDCRedirectURIHTML do
|
||||
use ComfycampWeb, :html
|
||||
|
||||
def new(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<.header>Новый redirect URI</.header>
|
||||
<.uri_form changeset={@changeset} action={~p"/admin/oidc_apps/#{@oidc_app}/redirect_uris"} />
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
def uri_form(assigns) do
|
||||
~H"""
|
||||
<.simple_form :let={f} for={@changeset} action={@action}>
|
||||
<.error :if={@changeset.action}>
|
||||
Что-то пошло не так
|
||||
</.error>
|
||||
<.input field={f[:uri]} type="url" label="Redirect URI" />
|
||||
<:actions>
|
||||
<.button>Сохранить</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
||||
"""
|
||||
end
|
||||
end
|
|
@ -15,7 +15,6 @@ defmodule ComfycampWeb.Router do
|
|||
|
||||
pipeline :api do
|
||||
plug :accepts, ["json"]
|
||||
plug :fetch_bearer_token
|
||||
end
|
||||
|
||||
scope "/", ComfycampWeb do
|
||||
|
@ -29,20 +28,6 @@ defmodule ComfycampWeb.Router do
|
|||
get "/cinema", CinemaController, :index
|
||||
end
|
||||
|
||||
scope "/", ComfycampWeb do
|
||||
pipe_through :api
|
||||
|
||||
post "/oauth/token", OauthController, :token
|
||||
get "/.well-known/openid-configuration", OauthController, :openid_discovery
|
||||
end
|
||||
|
||||
scope "/", ComfycampWeb do
|
||||
pipe_through [:api, :require_oauth]
|
||||
|
||||
get "/oauth/userinfo", OauthController, :user_info
|
||||
post "/oauth/userinfo", OauthController, :user_info
|
||||
end
|
||||
|
||||
# Enable LiveDashboard and Swoosh mailbox preview in development
|
||||
if Application.compile_env(:comfycamp, :dev_routes) do
|
||||
# If you want to use the LiveDashboard in production, you should put
|
||||
|
@ -88,13 +73,6 @@ defmodule ComfycampWeb.Router do
|
|||
|
||||
scope "/", ComfycampWeb do
|
||||
pipe_through [:browser]
|
||||
|
||||
scope "/" do
|
||||
pipe_through [:require_authenticated_user]
|
||||
|
||||
get "/oauth/authorize", OauthController, :authorize
|
||||
post "/oauth/generate_code", OauthController, :generate_code
|
||||
end
|
||||
end
|
||||
|
||||
scope "/", ComfycampWeb do
|
||||
|
@ -120,10 +98,6 @@ defmodule ComfycampWeb.Router do
|
|||
|
||||
resources "/notes", NotesEditorController
|
||||
resources "/users", UserEditorController, only: [:index, :show]
|
||||
resources "/oidc_apps", OIDCAppController
|
||||
|
||||
resources "/oidc_apps/:client_id/redirect_uris", OIDCRedirectURIController,
|
||||
only: [:new, :create, :delete]
|
||||
|
||||
put "/users/:id/approve", UserEditorController, :approve
|
||||
put "/users/:id/disapprove", UserEditorController, :disapprove
|
||||
|
|
|
@ -110,37 +110,6 @@ defmodule ComfycampWeb.UserAuth do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Authenticate the user by looking at Authorization header
|
||||
and checking Bearer token.
|
||||
"""
|
||||
def fetch_bearer_token(conn, _opts) do
|
||||
case Plug.Conn.get_req_header(conn, "authorization") do
|
||||
["Bearer " <> b64token] ->
|
||||
token = Base.url_decode64!(b64token)
|
||||
user = Accounts.get_user_by_bearer_token(token)
|
||||
assign(conn, :oauth_user, user)
|
||||
|
||||
_ ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Check that request contains a valid bearer token.
|
||||
Call after fetch_bearer_token plug.
|
||||
"""
|
||||
def require_oauth(conn, _opts) do
|
||||
if conn.assigns[:oauth_user] do
|
||||
conn
|
||||
else
|
||||
conn
|
||||
|> put_status(:unauthorized)
|
||||
|> json(%{error: "You must use a bearer token to access this page."})
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Handles mounting and authenticating the current_user in LiveViews.
|
||||
|
||||
|
|
9
priv/repo/migrations/20241020180106_delete_openid.exs
Normal file
9
priv/repo/migrations/20241020180106_delete_openid.exs
Normal file
|
@ -0,0 +1,9 @@
|
|||
defmodule Comfycamp.Repo.Migrations.DeleteOpenid do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
drop table(:oidc_redirect_uris)
|
||||
drop table(:oidc_codes)
|
||||
drop table(:oidc_apps)
|
||||
end
|
||||
end
|
|
@ -1,72 +0,0 @@
|
|||
defmodule Comfycamp.SSOTest do
|
||||
use Comfycamp.DataCase
|
||||
|
||||
alias Comfycamp.SSO
|
||||
|
||||
describe "oidc_apps" do
|
||||
alias Comfycamp.SSO.OIDCApp
|
||||
|
||||
import Comfycamp.SSOFixtures
|
||||
|
||||
@invalid_attrs %{enabled: nil, name: nil, client_id: nil, client_secret: nil}
|
||||
|
||||
test "list_oidc_apps/0 returns all oidc_apps" do
|
||||
oidc_app = oidc_app_fixture()
|
||||
assert SSO.list_oidc_apps() == [oidc_app]
|
||||
end
|
||||
|
||||
test "get_oidc_app!/1 returns the oidc_app with given id" do
|
||||
oidc_app = oidc_app_fixture()
|
||||
assert SSO.get_oidc_app!(oidc_app.client_id).client_id == oidc_app.client_id
|
||||
end
|
||||
|
||||
test "create_oidc_app/1 with valid data creates a oidc_app" do
|
||||
valid_attrs = %{
|
||||
enabled: true,
|
||||
name: "some name",
|
||||
client_id: "some client_id",
|
||||
client_secret: "some client_secret"
|
||||
}
|
||||
|
||||
assert {:ok, %OIDCApp{} = oidc_app} = SSO.create_oidc_app(valid_attrs)
|
||||
assert oidc_app.enabled == true
|
||||
assert oidc_app.name == "some name"
|
||||
end
|
||||
|
||||
test "create_oidc_app/1 with invalid data returns error changeset" do
|
||||
assert {:error, %Ecto.Changeset{}} = SSO.create_oidc_app(@invalid_attrs)
|
||||
end
|
||||
|
||||
test "update_oidc_app/2 with valid data updates the oidc_app" do
|
||||
oidc_app = oidc_app_fixture()
|
||||
|
||||
update_attrs = %{
|
||||
enabled: false,
|
||||
name: "some updated name",
|
||||
client_id: "some updated client_id",
|
||||
client_secret: "some updated client_secret"
|
||||
}
|
||||
|
||||
assert {:ok, %OIDCApp{} = oidc_app} = SSO.update_oidc_app(oidc_app, update_attrs)
|
||||
assert oidc_app.enabled == false
|
||||
assert oidc_app.name == "some updated name"
|
||||
end
|
||||
|
||||
test "update_oidc_app/2 with invalid data returns error changeset" do
|
||||
oidc_app = oidc_app_fixture()
|
||||
assert {:error, %Ecto.Changeset{}} = SSO.update_oidc_app(oidc_app, @invalid_attrs)
|
||||
assert oidc_app.client_id == SSO.get_oidc_app!(oidc_app.client_id).client_id
|
||||
end
|
||||
|
||||
test "delete_oidc_app/1 deletes the oidc_app" do
|
||||
oidc_app = oidc_app_fixture()
|
||||
assert {:ok, %OIDCApp{}} = SSO.delete_oidc_app(oidc_app)
|
||||
assert_raise Ecto.NoResultsError, fn -> SSO.get_oidc_app!(oidc_app.client_id) end
|
||||
end
|
||||
|
||||
test "change_oidc_app/1 returns a oidc_app changeset" do
|
||||
oidc_app = oidc_app_fixture()
|
||||
assert %Ecto.Changeset{} = SSO.change_oidc_app(oidc_app)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,100 +0,0 @@
|
|||
defmodule ComfycampWeb.OIDCAppControllerTest do
|
||||
use ComfycampWeb.ConnCase
|
||||
|
||||
import Comfycamp.SSOFixtures
|
||||
|
||||
@create_attrs %{
|
||||
client_id: "some_client_id",
|
||||
client_secret: "some client_secret",
|
||||
name: "some name",
|
||||
enabled: true
|
||||
}
|
||||
@update_attrs %{
|
||||
client_id: "some_client_id",
|
||||
client_secret: "some updated client_secret",
|
||||
name: "some updated name",
|
||||
enabled: false
|
||||
}
|
||||
@invalid_attrs %{enabled: nil, name: nil, client_id: nil, client_secret: nil}
|
||||
|
||||
describe "index" do
|
||||
setup [:register_and_log_in_admin_user]
|
||||
|
||||
test "lists all oidc_apps", %{conn: conn} do
|
||||
conn = get(conn, ~p"/admin/oidc_apps")
|
||||
assert html_response(conn, 200) =~ "Listing OpenID Connect apps"
|
||||
end
|
||||
end
|
||||
|
||||
describe "new oidc_app" do
|
||||
setup [:register_and_log_in_admin_user]
|
||||
|
||||
test "renders form", %{conn: conn} do
|
||||
conn = get(conn, ~p"/admin/oidc_apps/new")
|
||||
assert html_response(conn, 200) =~ "New OpenID Connect app"
|
||||
end
|
||||
end
|
||||
|
||||
describe "create oidc_app" do
|
||||
setup [:register_and_log_in_admin_user]
|
||||
|
||||
test "redirects to show when data is valid", %{conn: conn} do
|
||||
conn = post(conn, ~p"/admin/oidc_apps", oidc_app: @create_attrs)
|
||||
|
||||
assert %{id: client_id} = redirected_params(conn)
|
||||
assert redirected_to(conn) == ~p"/admin/oidc_apps/#{client_id}"
|
||||
|
||||
conn = get(conn, ~p"/admin/oidc_apps/#{client_id}")
|
||||
assert html_response(conn, 200) =~ "OpenID app"
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn} do
|
||||
conn = post(conn, ~p"/admin/oidc_apps", oidc_app: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "New OpenID Connect app"
|
||||
end
|
||||
end
|
||||
|
||||
describe "edit oidc_app" do
|
||||
setup [:create_oidc_app, :register_and_log_in_admin_user]
|
||||
|
||||
test "renders form for editing chosen oidc_app", %{conn: conn, oidc_app: oidc_app} do
|
||||
conn = get(conn, ~p"/admin/oidc_apps/#{oidc_app}/edit")
|
||||
assert html_response(conn, 200) =~ "Edit OpenID app"
|
||||
end
|
||||
end
|
||||
|
||||
describe "update oidc_app" do
|
||||
setup [:create_oidc_app, :register_and_log_in_admin_user]
|
||||
|
||||
test "redirects when data is valid", %{conn: conn, oidc_app: oidc_app} do
|
||||
conn = put(conn, ~p"/admin/oidc_apps/#{oidc_app}", oidc_app: @update_attrs)
|
||||
assert redirected_to(conn) == ~p"/admin/oidc_apps/#{oidc_app}"
|
||||
|
||||
conn = get(conn, ~p"/admin/oidc_apps/#{oidc_app}")
|
||||
assert html_response(conn, 200) =~ "some updated name"
|
||||
end
|
||||
|
||||
test "renders errors when data is invalid", %{conn: conn, oidc_app: oidc_app} do
|
||||
conn = put(conn, ~p"/admin/oidc_apps/#{oidc_app}", oidc_app: @invalid_attrs)
|
||||
assert html_response(conn, 200) =~ "Edit OpenID app"
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete oidc_app" do
|
||||
setup [:create_oidc_app, :register_and_log_in_admin_user]
|
||||
|
||||
test "deletes chosen oidc_app", %{conn: conn, oidc_app: oidc_app} do
|
||||
conn = delete(conn, ~p"/admin/oidc_apps/#{oidc_app}")
|
||||
assert redirected_to(conn) == ~p"/admin/oidc_apps"
|
||||
|
||||
assert_error_sent 404, fn ->
|
||||
get(conn, ~p"/admin/oidc_apps/#{oidc_app}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp create_oidc_app(_) do
|
||||
oidc_app = oidc_app_fixture()
|
||||
%{oidc_app: oidc_app}
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
defmodule Comfycamp.SSOFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `Comfycamp.SSO` context.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Generate a oidc_app.
|
||||
"""
|
||||
def oidc_app_fixture(attrs \\ %{}) do
|
||||
{:ok, oidc_app} =
|
||||
attrs
|
||||
|> Enum.into(%{
|
||||
client_id: "some_client_id",
|
||||
client_secret: "some client_secret",
|
||||
name: "some name",
|
||||
enabled: true
|
||||
})
|
||||
|> Comfycamp.SSO.create_oidc_app()
|
||||
|
||||
oidc_app
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue