2 Commits

  1. 9
      CHANGELOG.md
  2. 16
      lib/mirage/accounts.ex
  3. 5
      lib/mirage/accounts/user.ex
  4. 1
      lib/mirage/links/link.ex
  5. 1
      lib/mirage/notes/note.ex
  6. 13
      lib/mirage_web/controllers/api/v1/note_controller.ex
  7. 31
      lib/mirage_web/controllers/user_auth.ex
  8. 15
      lib/mirage_web/router.ex
  9. 6
      lib/mirage_web/templates/user_settings/edit.html.eex
  10. 2
      mix.exs
  11. 1
      priv/repo/migrations/20210330060742_create_user_identities.exs
  12. 9
      priv/repo/migrations/20210331200224_add_api_key_to_user.exs
  13. 16
      test/mirage/accounts_test.exs
  14. 15
      test/mirage_web/live/user_identity_live_test.exs

9
CHANGELOG.md

@ -5,6 +5,15 @@ See [Conventional Commits](Https://conventionalcommits.org) for commit guideline
<!-- changelog -->
## [v0.101.0](https://git.inhji.de/inhji/mirage/compare/v0.100.0...v0.101.0) (2021-03-31)
### Features:
* add api key to user
## [v0.100.0](https://git.inhji.de/inhji/mirage/compare/v0.99.0...v0.100.0) (2021-03-30)

16
lib/mirage/accounts.ex

@ -25,6 +25,22 @@ defmodule Mirage.Accounts do
Repo.get_by(User, email: email)
end
@doc """
Gets a user by their API Key.
## Examples
iex> get_user_by_email("ff775fb2-e4ef-432c-b672-8df7eabb7c1e")
%User{}
iex> get_user_by_email("d1c6191a-86e7-4a3d-9403-e14de4743b2d")
nil
"""
def get_user_by_api_key(api_key) when is_binary(api_key) do
Repo.get_by(User, api_key: api_key)
end
@doc """
Gets a user by email and password.

5
lib/mirage/accounts/user.ex

@ -8,6 +8,7 @@ defmodule Mirage.Accounts.User do
field :name, :string
field :real_name, :string
field :notes, :string
field :api_key, :string
field :password, :string, virtual: true
field :hashed_password, :string
@ -77,8 +78,8 @@ defmodule Mirage.Accounts.User do
"""
def profile_changeset(changeset, attrs) do
changeset
|> cast(attrs, [:name, :real_name, :notes])
|> validate_required([:name, :real_name, :notes])
|> cast(attrs, [:name, :real_name, :notes, :api_key])
|> validate_required([:name, :real_name, :notes, :api_key])
end
@doc """

1
lib/mirage/links/link.ex

@ -2,6 +2,7 @@ defmodule Mirage.Links.Link do
use Ecto.Schema
import Ecto.Changeset
@derive {Jason.Encoder, only: [:title, :content, :content_html, :url, :domain]}
schema "links" do
field :title, :string
field :content, :string

1
lib/mirage/notes/note.ex

@ -2,6 +2,7 @@ defmodule Mirage.Notes.Note do
use Ecto.Schema
import Ecto.Changeset
@derive {Jason.Encoder, only: [:title, :content, :content_html, :links]}
schema "notes" do
field :title, :string
field :content, :string

13
lib/mirage_web/controllers/api/v1/note_controller.ex

@ -0,0 +1,13 @@
defmodule MirageWeb.API.V1.NoteController do
use MirageWeb, :controller
alias Mirage.Notes
def get_daily(conn, _params) do
daily_note = Notes.list_notes(:today) |> List.first()
json(conn, %{
data: %{note: daily_note}
})
end
end

31
lib/mirage_web/controllers/user_auth.ex

@ -11,6 +11,7 @@ defmodule MirageWeb.UserAuth do
@max_age 60 * 60 * 24 * 60
@remember_me_cookie "_mirage_web_user_remember_me"
@remember_me_options [sign: true, max_age: @max_age, same_site: "Lax"]
@api_key_header "X-API-Key"
@doc """
Logs the user in.
@ -97,6 +98,26 @@ defmodule MirageWeb.UserAuth do
|> put_session(:user_id, user && user.id)
end
@doc """
Authenticates the user by checking the headers for an API key.
"""
def fetch_session_from_api_key(conn, _opts) do
case get_req_header(conn, @api_key_header) do
[api_key] ->
user = Accounts.get_user_by_api_key(api_key)
if user do
conn
|> put_session(:current_user, user)
else
conn
end
_ ->
conn
end
end
defp ensure_user_token(conn) do
if user_token = get_session(conn, :user_token) do
{user_token, conn}
@ -142,6 +163,16 @@ defmodule MirageWeb.UserAuth do
end
end
def require_api_key(conn, _opts) do
if conn.assigns[:current_user] do
conn
else
conn
|> json(%{error: "Not Authenticated"})
|> halt()
end
end
defp maybe_store_return_to(%{method: "GET"} = conn) do
put_session(conn, :user_return_to, current_path(conn))
end

15
lib/mirage_web/router.ex

@ -27,6 +27,13 @@ defmodule MirageWeb.Router do
pipeline :api do
plug :accepts, ["json"]
plug :fetch_session_from_api_key
plug :require_api_key
end
pipeline :api_dev do
plug :accepts, ["json"]
plug :fetch_session_from_api_key
end
scope "/", MirageWeb do
@ -130,4 +137,12 @@ defmodule MirageWeb.Router do
post "/user/confirm", UserConfirmationController, :create
get "/user/confirm/:token", UserConfirmationController, :confirm
end
scope "/api", MirageWeb.API, as: :api do
pipe_through [:api_dev]
scope "/v1", V1, as: :v1 do
get "/notes/daily", NoteController, :get_daily
end
end
end

6
lib/mirage_web/templates/user_settings/edit.html.eex

@ -31,6 +31,12 @@
<%= error_tag f, :notes %>
</fieldset>
<fieldset>
<%= label f, :api_key %>
<%= text_input f, :api_key, required: true, pattern: "[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}", title: "GUID" %>
<%= error_tag f, :api_key %>
</fieldset>
<div>
<%= submit "Change Profile" %>
</div>

2
mix.exs

@ -1,7 +1,7 @@
defmodule Mirage.MixProject do
use Mix.Project
@version "0.100.0"
@version "0.101.0"
def project do
[

1
priv/repo/migrations/20210330060742_create_user_identities.exs

@ -9,6 +9,5 @@ defmodule Mirage.Repo.Migrations.CreateUserIdentities do
timestamps()
end
end
end

9
priv/repo/migrations/20210331200224_add_api_key_to_user.exs

@ -0,0 +1,9 @@
defmodule Mirage.Repo.Migrations.AddApiKeyToUser do
use Ecto.Migration
def change do
alter table(:users) do
add :api_key, :string
end
end
end

16
test/mirage/accounts_test.exs

@ -506,7 +506,11 @@ defmodule Mirage.AccountsTest do
alias Mirage.Accounts.UserIdentity
@valid_attrs %{name: "some name", rel: "some rel", value: "some value"}
@update_attrs %{name: "some updated name", rel: "some updated rel", value: "some updated value"}
@update_attrs %{
name: "some updated name",
rel: "some updated rel",
value: "some updated value"
}
@invalid_attrs %{name: nil, rel: nil, value: nil}
def user_identity_fixture(attrs \\ %{}) do
@ -541,7 +545,10 @@ defmodule Mirage.AccountsTest do
test "update_user_identity/2 with valid data updates the user_identity" do
user_identity = user_identity_fixture()
assert {:ok, %UserIdentity{} = user_identity} = Accounts.update_user_identity(user_identity, @update_attrs)
assert {:ok, %UserIdentity{} = user_identity} =
Accounts.update_user_identity(user_identity, @update_attrs)
assert user_identity.name == "some updated name"
assert user_identity.rel == "some updated rel"
assert user_identity.value == "some updated value"
@ -549,7 +556,10 @@ defmodule Mirage.AccountsTest do
test "update_user_identity/2 with invalid data returns error changeset" do
user_identity = user_identity_fixture()
assert {:error, %Ecto.Changeset{}} = Accounts.update_user_identity(user_identity, @invalid_attrs)
assert {:error, %Ecto.Changeset{}} =
Accounts.update_user_identity(user_identity, @invalid_attrs)
assert user_identity == Accounts.get_user_identity!(user_identity.id)
end

15
test/mirage_web/live/user_identity_live_test.exs

@ -54,7 +54,9 @@ defmodule MirageWeb.UserIdentityLiveTest do
test "updates user_identity in listing", %{conn: conn, user_identity: user_identity} do
{:ok, index_live, _html} = live(conn, Routes.user_identity_index_path(conn, :index))
assert index_live |> element("#user_identity-#{user_identity.id} a", "Edit") |> render_click() =~
assert index_live
|> element("#user_identity-#{user_identity.id} a", "Edit")
|> render_click() =~
"Edit User identity"
assert_patch(index_live, Routes.user_identity_index_path(conn, :edit, user_identity))
@ -76,7 +78,10 @@ defmodule MirageWeb.UserIdentityLiveTest do
test "deletes user_identity in listing", %{conn: conn, user_identity: user_identity} do
{:ok, index_live, _html} = live(conn, Routes.user_identity_index_path(conn, :index))
assert index_live |> element("#user_identity-#{user_identity.id} a", "Delete") |> render_click()
assert index_live
|> element("#user_identity-#{user_identity.id} a", "Delete")
|> render_click()
refute has_element?(index_live, "#user_identity-#{user_identity.id}")
end
end
@ -85,14 +90,16 @@ defmodule MirageWeb.UserIdentityLiveTest do
setup [:create_user_identity]
test "displays user_identity", %{conn: conn, user_identity: user_identity} do
{:ok, _show_live, html} = live(conn, Routes.user_identity_show_path(conn, :show, user_identity))
{:ok, _show_live, html} =
live(conn, Routes.user_identity_show_path(conn, :show, user_identity))
assert html =~ "Show User identity"
assert html =~ user_identity.name
end
test "updates user_identity within modal", %{conn: conn, user_identity: user_identity} do
{:ok, show_live, _html} = live(conn, Routes.user_identity_show_path(conn, :show, user_identity))
{:ok, show_live, _html} =
live(conn, Routes.user_identity_show_path(conn, :show, user_identity))
assert show_live |> element("a", "Edit") |> render_click() =~
"Edit User identity"

Loading…
Cancel
Save