Skip to content

Commit

Permalink
Merge pull request #175 from dwyl/seeds-rework-#174
Browse files Browse the repository at this point in the history
Seeds Rework #174
  • Loading branch information
SimonLab authored Feb 1, 2022
2 parents 6466f8b + 134f1a3 commit ead7450
Show file tree
Hide file tree
Showing 25 changed files with 306 additions and 186 deletions.
20 changes: 15 additions & 5 deletions .env_sample
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
export [email protected]
export AUTH_API_KEY=2PzB7PPnpuLsbWmWtXpGyI+kfSQSQ1zUW2Atz/+8PdZuSEJzHgzGnJWV35nTKRwx/dwylauth.herokuapp.com
export EMAIL_APP_URL=https://dwylmail.herokuapp.com
export [email protected]
export AUTH_URL=dwylauth.herokuapp.com
export SECRET_KEY_BASE=2PzB7PPnpuLsbWmWtXpGyI+kfSQSQ1zUW2Atz/+8PdZuSEJzHgzGnJWV35nTKRwx
export ENCRYPTION_KEYS='nMdayQpR0aoasLaq1g94FLba+A+wB44JLko47sVQXMg=,L+ZVX8iheoqgqb22mUpATmMDsvVGtafoAeb0KN5uWf0='

# We use these two Oauth2 Providers:
# https://github.com/dwyl/elixir-auth-github
export GITHUB_CLIENT_ID=CreateGitHubApp
export GITHUB_CLIENT_SECRET=SuperSecret

# https://github.com/dwyl/elixir-auth-google
export GOOGLE_CLIENT_ID=YourAppsClientId.apps.googleusercontent.com
export GOOGLE_CLIENT_SECRET=SuperSecret
export SECRET_KEY_BASE=2PzB7PPnpuLsbWmWtXpGyI+kfSQSQ1zUW2Atz/+8PdZuSEJzHgzGnJWV35nTKRwx
export ENCRYPTION_KEYS='nMdayQpR0aoasLaq1g94FLba+A+wB44JLko47sVQXMg=,L+ZVX8iheoqgqb22mUpATmMDsvVGtafoAeb0KN5uWf0='

# Optional for sending emails:
export EMAIL_APP_URL=https://dwylmail.herokuapp.com

# Export AUTH_API_KEY once you have run Auth.Init.main/0
export AUTH_API_KEY=2PzB7PPnpuLsbWmWtXpGyI+kfSQSQ1zUW2Atz/+8PdZuSEJzHgzGnJWV35nTKRwx/dwylauth.herokuapp.com
2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ config :esbuild,
args: ~w(js/app.js --bundle --target=es2016 --outdir=../priv/static/assets),
cd: Path.expand("../assets", __DIR__),
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
]
]
3 changes: 2 additions & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ config :auth, AuthWeb.Endpoint,
# Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]}
]

# ## SSL Support
#
# In order to use HTTPS in development, a self-signed
Expand Down Expand Up @@ -67,4 +68,4 @@ config :logger, :console, format: "[$level] $message\n"
config :phoenix, :stacktrace_depth, 20

# Initialize plugs at runtime for faster development compilation
config :phoenix, :plug_init_mode, :runtime
config :phoenix, :plug_init_mode, :runtime
7 changes: 4 additions & 3 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ config :auth, Auth.Repo,
# you can enable the server option below.
config :auth, AuthWeb.Endpoint,
http: [port: 4002],
server: true # https://elixirforum.com/t/wallaby-with-phoenix-1-16-rc0/42352/9
#  https://elixirforum.com/t/wallaby-with-phoenix-1-16-rc0/42352/9
server: true

# Print only warnings and errors during test
config :logger, level: :warn
Expand All @@ -24,5 +25,5 @@ config :elixir_auth_google,

config :auth_plug,
api_key: System.get_env("AUTH_API_KEY"),
# "2PzB7PPnpuLsbWmWtXpGyI+kfSQSQ1zUW2Atz/+8PdZuSEJzHgzGnJWV35nTKRwx/authdev.herokuapp.com",
httpoison_mock: true
# "2PzB7PPnpuLsbWmWtXpGyI+kfSQSQ1zUW2Atz/+8PdZuSEJzHgzGnJWV35nTKRwx/authdev.herokuapp.com",
httpoison_mock: true
1 change: 1 addition & 0 deletions lib/auth/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ defmodule Auth.Application do
AuthWeb.Endpoint.config_change(changed, removed)
:ok
end

# coveralls-ignore-stop
end
52 changes: 52 additions & 0 deletions lib/auth/init/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<div align="center">

# Welcome! 👋

![why](https://user-images.githubusercontent.com/194400/150698837-4eab1188-0aae-4dfd-b9c1-56ce0d311d20.png)

</div>

The purpose of the **`Auth` Application**
is to have a complete separation of concerns between
our
[**App**](https://github.com/dwyl/app)
and any Authentication/Authorization code
in order to: <br />

**a)** ***Simplify*** the **code** in the _main_
[**App**](https://github.com/dwyl/app)
because there is no "User Management"
to think about.

**b)** ***Maximize privacy/security*** of any/all **personal data**
that people using our App entrust in us
by storing it in a totally separate
fully encrypted database.

**c)** Minimize the number of environment variables
in the main App so that _anyone_ can run it
from scratch in less than 2 minutes.


For better or worse,
minimizing the number of environment variables
in the _main_
[**App**](https://github.com/dwyl/app)
means they have to go _somewhere_ ...
that somewhere is right _here_!

## Required Environment Variables for `Auth` App

In order to initialize the **`Auth` Application**
+ `ADMIN_EMAIL` - the email address of the person who will
administer the **`Auth` App**.
+ `AUTH_URL` - the base URL where the application will be hosted,
e.g: `"auth.dwyl.com"` (exclude the protocol)
+ `SECRET_KEY_BASE` - the secret Phoenix uses to sign and encrypt important information.
see:
https://hexdocs.pm/phoenix/deployment.html#handling-of-your-application-secrets
+ `ENCRYPTION_KEYS` - a list of one or more encryption keys
used to encrypt data in the database.
see: `.env_sample` for example.


File renamed without changes.
126 changes: 126 additions & 0 deletions lib/auth/init/init.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
defmodule Auth.Init do
@moduledoc """
`Init` as its' name suggests initializes the Auth Application
by creating the necessary records in the various tables.
This is the sequence of steps that are followed to init the App:
1. Create the "Super Admin" person who owns the Auth App
based on the `ADMIN_EMAIL` environment/config variable.
> The person.id (1) for the Super Admin
will own the remaining records so it needs to be created first.
2. Create default records (Statuses & Roles)
3. Create the App and `AUTH_API_KEY` for the Auth App.
> Log the `AUTH_API_KEY` so that it can be exported.
"""

require Logger
import Ecto.Changeset
alias Auth.{Person, Role, Repo, Status}

def main do
Logger.info("Initialising the Auth Database ...")
# check required environment variables:
Envar.is_set_all?(~w/ADMIN_EMAIL AUTH_URL ENCRYPTION_KEYS SECRET_KEY_BASE/)

admin = Auth.Init.create_admin()

Auth.Init.insert_statuses()
Auth.Init.create_default_roles()

api_key = Auth.Init.create_apikey_for_admin(admin)

case Mix.env() do
:test ->
# set the AUTH_API_KEY environment variable during test run:
Envar.set("AUTH_API_KEY", api_key)

# ignore the next lines because we can't test them:
# coveralls-ignore-start
_ ->
# Log the AUTH_API_KEY so it can be exported:
Logger.info("export AUTH_API_KEY=#{api_key}")
# coveralls-ignore-stop
end

# Update status of Admin to "verified"
Auth.Person.verify_person_by_id(admin.id)

# grant superadmin role to app owner:
Auth.PeopleRoles.upsert(1, 1, 1, 1)

:ok
end

# Get AUTH_URL or fallback to localhost:
defp get_auth_url do
# see .env_sample for example
Envar.get("AUTH_URL") || "localhost:4000"
end

def create_admin do
email = Envar.get("ADMIN_EMAIL")

case Person.get_person_by_email(email) do
# Ignore if the Super Admin already exists:
# coveralls-ignore-start
nil ->
%Person{}
|> Person.changeset(%{email: email})
|> Repo.insert!()

# coveralls-ignore-stop

person ->
person
end
end

def create_apikey_for_admin(person) do
{:ok, app} =
%{
"name" => "default system app",
"desc" => "Created by lib/auth/init/init.ex during setup.",
"url" => "localhost:4000",
"person_id" => person.id,
"status" => 3
}
|> Auth.App.create_app()

# If AUTH_API_KEY environment variable is already set, use it:
update_attrs = %{
"client_id" => AuthPlug.Token.client_id(),
"client_secret" => AuthPlug.Token.client_secret()
}

{:ok, key} =
Auth.Apikey.get_apikey_by_app_id(app.id)
|> cast(update_attrs, [:client_id, :client_secret])
|> Repo.update()

key.client_id <> "/" <> key.client_secret <> "/" <> get_auth_url()
end

# scripts for creating default roles and permissions
def get_json(filepath) do
path = File.cwd!() <> filepath
{:ok, data} = File.read(path)
json = Jason.decode!(data)
json
end

def create_default_roles do
Enum.each(get_json("/lib/auth/init/default_roles.json"), fn role ->
Role.upsert_role(role)
end)
end

def insert_statuses do
Enum.each(get_json("/lib/auth/init/statuses.json"), fn status ->
Status.upsert_status(status)
end)
end
end
File renamed without changes.
26 changes: 24 additions & 2 deletions lib/auth/people_roles.ex
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ defmodule Auth.PeopleRoles do
end

@doc """
insert/4 grants a role to the given person
`insert/4` grants a role to the given person
app_id for app the person is granted the role for (always scoped to app!)
grantee_id is the person.id of the person being granted the role
granter_id is the id of the person (admin) granting the role
Expand All @@ -97,7 +97,29 @@ defmodule Auth.PeopleRoles do
end

@doc """
revoke/3 grants a role to the given person
`upsert/4` grants a role (`role_id`) to the given person (`grantee_id`)
for the `app_id`.
`granter_id` is the id of the person (admin) granting the role.
"""
def upsert(app_id, grantee_id, granter_id, role_id) do
case get_roles_for_person_for_app(app_id, grantee_id) do
# if there are no roles for the person, insert it:
n when n in [nil, []] ->
[insert(app_id, grantee_id, granter_id, role_id)]
roles ->
# if the role exists in the list of roles, return the list
if Enum.find_value(roles, fn r -> r.id == role_id end) do
roles
else
[insert(app_id, grantee_id, granter_id, role_id)]
end
end
end


@doc """
`revoke/3` grants a role to the given person
revoker_id is the id of the person (admin) granting the role
person_id is the person.id of the person being granted the role
role_id is the role.id (int, e.g: 4) of th role being granted.
Expand Down
24 changes: 11 additions & 13 deletions lib/auth/session.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,23 @@ defmodule Auth.Session do
assign(conn, :sid, session.id)
end


@doc """
`get/1` retrieves the current session from DB
based on conn.assigns.person data.
See tests: test/auth/session_test.exs for sample usage.
"""
def get(conn) do
Repo.one(
from s in __MODULE__,
where: s.app_id == ^conn.assigns.person.app_id
and
s.person_id == ^conn.assigns.person.id
and # match on UA in case person has multiple devices/sessions
s.user_agent_id == ^Auth.UserAgent.get_user_agent_id(conn)
and # only the sessions that haven't been "ended"
is_nil(s.end),
# sort by most recent in case there are older un-ended sessions:
order_by: [desc: :inserted_at]
from s in __MODULE__,
# match on UA in case person has multiple devices/sessions
#  only the sessions that haven't been "ended"
where:
s.app_id == ^conn.assigns.person.app_id and
s.person_id == ^conn.assigns.person.id and
s.user_agent_id == ^Auth.UserAgent.get_user_agent_id(conn) and
is_nil(s.end),
# sort by most recent in case there are older un-ended sessions:
order_by: [desc: :inserted_at]
)
end

Expand All @@ -83,7 +82,7 @@ defmodule Auth.Session do
def get_by_id(conn) do
Repo.one(
from s in __MODULE__,
where: s.id == ^conn.assigns.sid
where: s.id == ^conn.assigns.sid
)
end

Expand All @@ -96,7 +95,6 @@ defmodule Auth.Session do
|> Repo.update!()
end


@doc """
`end_session/1` update session to end it.
"""
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/status.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Auth.Status do
alias Auth.Repo
# https://stackoverflow.com/a/47501059/1148249
alias __MODULE__
@admin_email System.get_env("ADMIN_EMAIL")
@admin_email Envar.get("ADMIN_EMAIL")

schema "status" do
field :text, :string
Expand Down
1 change: 0 additions & 1 deletion lib/auth_web/controllers/api_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ defmodule AuthWeb.ApiController do
`end_session/2` logs the person out of their session.
"""
def end_session(conn, %{"client_id" => client_id, "session_id" => session_id}) do

case Auth.Apikey.decode_decrypt(client_id) do
{:error, _} ->
Auth.Log.error(conn, %{client_id: client_id})
Expand Down
2 changes: 1 addition & 1 deletion lib/auth_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ defmodule AuthWeb.Endpoint do
plug Plug.Head
plug Plug.Session, @session_options
plug AuthWeb.Router
end
end
5 changes: 4 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,11 @@ defmodule Auth.Mixfile do
# https://github.com/dwyl/elixir-auth-google
{:elixir_auth_google, "~> 1.6.1"},

# Check/get Environment Variables: https://github.com/dwyl/envar
{:envar, "~> 1.0.4"},

# https://github.com/dwyl/auth_plug
{:auth_plug, "~> 1.4.7"},
{:auth_plug, "~> 1.4.11 "},

# https://github.com/dwyl/rbac
{:rbac, "~> 0.5.3"},
Expand Down
3 changes: 2 additions & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
%{
"argon2_elixir": {:hex, :argon2_elixir, "2.4.0", "2a22ea06e979f524c53b42b598fc6ba38cdcbc977a155e33e057732cfb1fb311", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "4ea82e183cf8e7f66dab1f767fedcfe6a195e140357ef2b0423146b72e0a551d"},
"auth_plug": {:hex, :auth_plug, "1.4.7", "7e09f8d60488a5cd28a095423c2c7772454fc4c3135208bf287e6ff8ef2e371d", [:mix], [{:httpoison, "~> 1.8.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.2.2", [hex: :jason, repo: "hexpm", optional: false]}, {:joken, "~> 2.4.1", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.12.1", [hex: :plug, repo: "hexpm", optional: false]}, {:useful, "~> 0.4.0", [hex: :useful, repo: "hexpm", optional: false]}], "hexpm", "98faa2c2b8d5af42854c23d19c013cef13670c0dfbe2584c0761b105e50d33aa"},
"auth_plug": {:hex, :auth_plug, "1.4.11", "f6f35708e737f6d56aae20abd3d974e877d5fd7eedff4f68480354c239be4ac1", [:mix], [{:httpoison, "~> 1.8.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:joken, "~> 2.4.1", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.12.1", [hex: :plug, repo: "hexpm", optional: false]}, {:useful, "~> 0.4.0", [hex: :useful, repo: "hexpm", optional: false]}], "hexpm", "3ddc43065d095c4111f185f9ee1fa594b6d2cef1a620f315acd7e9ae8786f389"},
"b58": {:hex, :b58, "1.0.2", "d758f42d89fbe84052f18b2f310bed2a8c8c5b38dd0af86970026974cc7daeda", [:mix], [], "hexpm", "148e299e2753eae222b13f399f461a0c4910fed608b8723fca3c33d1d4f82c17"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"castore": {:hex, :castore, "0.1.13", "ccf3ab251ffaebc4319f41d788ce59a6ab3f42b6c27e598ad838ffecee0b04f9", [:mix], [], "hexpm", "a14a7eecfec7e20385493dbb92b0d12c5d77ecfd6307de10102d58c94e8c49c0"},
Expand All @@ -20,6 +20,7 @@
"elixir_auth_github": {:hex, :elixir_auth_github, "1.6.0", "f100dd3917b2395219d533e5a834eb450abff3e0a53eab36632d53335e1beb92", [:mix], [{:httpoison, "~> 1.8.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "abc700b484bc5dfd4d6a2aa2acbbe79a7fd8ce112ca28d2c864ede169790ab1f"},
"elixir_auth_google": {:hex, :elixir_auth_google, "1.6.1", "37b4812bf129628e61fc3c5403346cd24cd267750c57790b06bcd453daabbdab", [:mix], [{:httpoison, "~> 1.8.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "bd59482ca0369bd4f27a725d12bc12a0f575e65c0130bc3acce17b2a29c5e626"},
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
"envar": {:hex, :envar, "1.0.4", "3264a6b13d5f9bca9a16e82ed2311c0efd08f3371d3c6b42dc5abe9d01da59aa", [:mix], [], "hexpm", "d7856849080745b907e39b33b825448810bc2348e3f5982ee32f8007d9b3226e"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"esbuild": {:hex, :esbuild, "0.4.0", "9f17db148aead4cf1e6e6a584214357287a93407b5fb51a031f122b61385d4c2", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "b61e4e6b92ffe45e4ee4755a22de6211a67c67987dc02afb35a425a0add1d447"},
"ex_doc": {:hex, :ex_doc, "0.25.5", "ac3c5425a80b4b7c4dfecdf51fa9c23a44877124dd8ca34ee45ff608b1c6deb9", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "688cfa538cdc146bc4291607764a7f1fcfa4cce8009ecd62de03b27197528350"},
Expand Down
Loading

0 comments on commit ead7450

Please sign in to comment.