mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
Make all tests pass
I removed some of VPN/Wall settings (they are irrelevant once we move out gateway) along with port-based rules conditions (since we are moving to userspace wg).
This commit is contained in:
10
apps/domain/.formatter.exs
Normal file
10
apps/domain/.formatter.exs
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
import_deps: [
|
||||
:ecto,
|
||||
:plug
|
||||
],
|
||||
inputs: [
|
||||
"*.{heex,ex,exs}",
|
||||
"{lib,test,priv}/**/*.{heex,ex,exs}"
|
||||
]
|
||||
]
|
||||
39
apps/domain/.gitignore
vendored
Normal file
39
apps/domain/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# macOS cruft
|
||||
.DS_Store
|
||||
|
||||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where 3rd-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
cloudfire-*.tar
|
||||
|
||||
# If NPM crashes, it generates a log, let's ignore it too.
|
||||
npm-debug.log
|
||||
|
||||
# The directory NPM downloads your dependencies sources to.
|
||||
/assets/node_modules/
|
||||
/assets/lib/node_modules/
|
||||
/assets/bin/
|
||||
|
||||
# Since we are building assets from assets/,
|
||||
# we ignore priv/static. You may want to comment
|
||||
# this depending on your deployment strategy.
|
||||
/priv/static/dist/
|
||||
3
apps/domain/README.md
Normal file
3
apps/domain/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Domain
|
||||
|
||||
Phoenix app for managing Firezone.
|
||||
5
apps/domain/coveralls.json
Normal file
5
apps/domain/coveralls.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"skip_files": [
|
||||
"test"
|
||||
]
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
defmodule FzHttp.Application do
|
||||
use Application
|
||||
|
||||
def start(_type, _args) do
|
||||
supervision_tree_mode = FzHttp.Config.fetch_env!(:fz_http, :supervision_tree_mode)
|
||||
|
||||
result =
|
||||
supervision_tree_mode
|
||||
|> children()
|
||||
|> Supervisor.start_link(strategy: :one_for_one, name: __MODULE__.Supervisor)
|
||||
|
||||
:ok = after_start()
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# Tell Phoenix to update the endpoint configuration
|
||||
# whenever the application is updated.
|
||||
def config_change(changed, _new, removed) do
|
||||
FzHttpWeb.Endpoint.config_change(changed, removed)
|
||||
:ok
|
||||
end
|
||||
|
||||
# XXX: get rid off this
|
||||
defp children(:full) do
|
||||
[
|
||||
# Infrastructure services
|
||||
FzHttp.Repo,
|
||||
FzHttp.Vault,
|
||||
{Phoenix.PubSub, name: FzHttp.PubSub},
|
||||
{FzHttp.Notifications, name: FzHttp.Notifications},
|
||||
FzHttpWeb.Presence,
|
||||
|
||||
# Application
|
||||
{Postgrex.Notifications, [name: FzHttp.Repo.Notifications] ++ FzHttp.Repo.config()},
|
||||
FzHttp.Repo.Notifier,
|
||||
FzHttp.Server,
|
||||
FzHttp.VpnSessionScheduler,
|
||||
FzHttp.Auth,
|
||||
FzHttpWeb.Endpoint,
|
||||
|
||||
# Observability
|
||||
FzHttp.ConnectivityChecks,
|
||||
FzHttp.Telemetry
|
||||
]
|
||||
end
|
||||
|
||||
defp children(:test) do
|
||||
[
|
||||
# Infrastructure services
|
||||
FzHttp.Repo,
|
||||
FzHttp.Vault,
|
||||
{Phoenix.PubSub, name: FzHttp.PubSub},
|
||||
{FzHttp.Notifications, name: FzHttp.Notifications},
|
||||
FzHttpWeb.Presence,
|
||||
|
||||
# Application
|
||||
FzHttp.Server,
|
||||
FzHttp.Auth,
|
||||
FzHttpWeb.Endpoint,
|
||||
|
||||
# Observability
|
||||
FzHttp.ConnectivityChecks,
|
||||
FzHttp.Telemetry
|
||||
]
|
||||
end
|
||||
|
||||
defp children(:database) do
|
||||
[
|
||||
FzHttp.Repo,
|
||||
FzHttp.Vault
|
||||
]
|
||||
end
|
||||
|
||||
if Mix.env() == :prod do
|
||||
defp after_start do
|
||||
FzHttp.Config.validate_runtime_config!()
|
||||
end
|
||||
else
|
||||
defp after_start do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,7 +0,0 @@
|
||||
defmodule FzHttp.Encrypted.Binary do
|
||||
@moduledoc """
|
||||
Configures how to encrpyt Binaries to the DB.
|
||||
"""
|
||||
|
||||
use Cloak.Ecto.Binary, vault: FzHttp.Vault
|
||||
end
|
||||
@@ -1,7 +0,0 @@
|
||||
defmodule FzHttp.Encrypted.Map do
|
||||
@moduledoc """
|
||||
Configures how to encrpyt Maps to the DB.
|
||||
"""
|
||||
|
||||
use Cloak.Ecto.Map, vault: FzHttp.Vault
|
||||
end
|
||||
@@ -1,39 +0,0 @@
|
||||
defmodule FzHttp.Repo.Notifier do
|
||||
@moduledoc """
|
||||
Listens for Repo notifications and trigger events based on data changes.
|
||||
"""
|
||||
|
||||
use GenServer
|
||||
|
||||
alias FzHttp.Events
|
||||
alias FzHttp.Repo
|
||||
|
||||
@impl GenServer
|
||||
def init(state) do
|
||||
for subject <- ~w(devices rules users)a do
|
||||
{:ok, _ref} = Postgrex.Notifications.listen(Repo.Notifications, "#{subject}_changed")
|
||||
end
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_info({:notification, _pid, _ref, event, payload}, _state) do
|
||||
subject = String.split(event, "_") |> List.first()
|
||||
data = Jason.decode!(payload, keys: :atoms)
|
||||
|
||||
handle_event(subject, data)
|
||||
|
||||
{:noreply, :event_handled}
|
||||
end
|
||||
|
||||
def start_link(opts \\ []), do: GenServer.start_link(__MODULE__, opts)
|
||||
|
||||
def handle_event(subject, %{op: "INSERT"} = data) do
|
||||
Events.add(subject, data.row)
|
||||
end
|
||||
|
||||
def handle_event(subject, %{op: "DELETE"} = data) do
|
||||
Events.delete(subject, data.row)
|
||||
end
|
||||
end
|
||||
@@ -1,14 +0,0 @@
|
||||
defmodule FzHttp.Rules.Rule do
|
||||
use FzHttp, :schema
|
||||
|
||||
schema "rules" do
|
||||
field :action, Ecto.Enum, values: [:drop, :accept], default: :drop
|
||||
field :destination, FzHttp.Types.INET
|
||||
field :port_type, Ecto.Enum, values: [:tcp, :udp]
|
||||
field :port_range, FzHttp.Types.Int4Range
|
||||
|
||||
belongs_to :user, FzHttp.Users.User
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
||||
@@ -1,41 +0,0 @@
|
||||
defmodule FzHttp.Server do
|
||||
@moduledoc """
|
||||
Functions for other processes to interact with the FzHttp application
|
||||
"""
|
||||
use GenServer
|
||||
alias FzHttp.{Devices, Devices.StatsUpdater, Rules, Users}
|
||||
|
||||
def start_link(_) do
|
||||
# We're not storing state, simply providing an API
|
||||
GenServer.start_link(__MODULE__, nil, name: {:global, :fz_http_server})
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def init(state) do
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call(:load_peers, _from, state) do
|
||||
reply = {:ok, Devices.to_peer_list()}
|
||||
{:reply, reply, state}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call(:load_settings, _from, state) do
|
||||
reply =
|
||||
{:ok,
|
||||
%{
|
||||
users: Users.as_settings(),
|
||||
devices: Devices.as_settings(),
|
||||
rules: Rules.as_settings()
|
||||
}}
|
||||
|
||||
{:reply, reply, state}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call({:update_device_stats, stats}, _from, state) do
|
||||
{:reply, StatsUpdater.update(stats), state}
|
||||
end
|
||||
end
|
||||
@@ -1,21 +0,0 @@
|
||||
defimpl String.Chars, for: Postgrex.INET do
|
||||
def to_string(%Postgrex.INET{} = inet), do: FzHttp.Types.INET.to_string(inet)
|
||||
end
|
||||
|
||||
defimpl Phoenix.HTML.Safe, for: Postgrex.INET do
|
||||
def to_iodata(%Postgrex.INET{} = inet), do: FzHttp.Types.INET.to_string(inet)
|
||||
end
|
||||
|
||||
defimpl Jason.Encoder, for: Postgrex.INET do
|
||||
def encode(%Postgrex.INET{} = struct, opts) do
|
||||
Jason.Encode.string("#{struct}", opts)
|
||||
end
|
||||
end
|
||||
|
||||
defimpl String.Chars, for: FzHttp.Types.IPPort do
|
||||
def to_string(%FzHttp.Types.IPPort{} = ip_port), do: FzHttp.Types.IPPort.to_string(ip_port)
|
||||
end
|
||||
|
||||
defimpl Phoenix.HTML.Safe, for: FzHttp.Types.IPPort do
|
||||
def to_iodata(%FzHttp.Types.IPPort{} = ip_port), do: FzHttp.Types.IPPort.to_string(ip_port)
|
||||
end
|
||||
@@ -1,7 +0,0 @@
|
||||
defmodule FzHttp.Vault do
|
||||
@moduledoc """
|
||||
Manages encrypted DB fields.
|
||||
"""
|
||||
|
||||
use Cloak.Vault, otp_app: :fz_http
|
||||
end
|
||||
@@ -1,27 +0,0 @@
|
||||
defmodule FzHttp.VpnSessionScheduler do
|
||||
@moduledoc """
|
||||
Checks for VPN sessions to expire.
|
||||
"""
|
||||
use GenServer
|
||||
|
||||
alias FzHttp.Events
|
||||
|
||||
# 1 minute
|
||||
@interval 60 * 1_000
|
||||
|
||||
def start_link(_) do
|
||||
GenServer.start_link(__MODULE__, %{})
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def init(state) do
|
||||
:timer.send_interval(@interval, :perform)
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_info(:perform, state) do
|
||||
Events.set_config()
|
||||
{:noreply, state}
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,9 @@
|
||||
defmodule FzHttp do
|
||||
defmodule Domain do
|
||||
@moduledoc """
|
||||
This module provides a common interface for all the domain modules,
|
||||
making sure our code structure is consistent and predictable.
|
||||
"""
|
||||
|
||||
def schema do
|
||||
quote do
|
||||
use Ecto.Schema
|
||||
@@ -15,7 +20,7 @@ defmodule FzHttp do
|
||||
def changeset do
|
||||
quote do
|
||||
import Ecto.Changeset
|
||||
import FzHttp.Validator
|
||||
import Domain.Validator
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.ApiTokens do
|
||||
alias FzHttp.{Repo, Validator, Auth}
|
||||
alias FzHttp.Users
|
||||
alias FzHttp.ApiTokens.Authorizer
|
||||
alias FzHttp.ApiTokens.ApiToken
|
||||
defmodule Domain.ApiTokens do
|
||||
alias Domain.{Repo, Validator, Auth}
|
||||
alias Domain.Users
|
||||
alias Domain.ApiTokens.Authorizer
|
||||
alias Domain.ApiTokens.ApiToken
|
||||
|
||||
def count_by_user_id(user_id) do
|
||||
ApiToken.Query.by_user_id(user_id)
|
||||
@@ -112,7 +112,7 @@ defmodule FzHttp.ApiTokens do
|
||||
changeset = ApiToken.Changeset.create_changeset(user, attrs, max: count_by_user_id)
|
||||
|
||||
with {:ok, api_token} <- Repo.insert(changeset) do
|
||||
FzHttp.Telemetry.create_api_token()
|
||||
Domain.Telemetry.create_api_token()
|
||||
{:ok, api_token}
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.ApiTokens.ApiToken do
|
||||
use FzHttp, :schema
|
||||
defmodule Domain.ApiTokens.ApiToken do
|
||||
use Domain, :schema
|
||||
|
||||
schema "api_tokens" do
|
||||
field :expires_at, :utc_datetime_usec
|
||||
@@ -7,7 +7,7 @@ defmodule FzHttp.ApiTokens.ApiToken do
|
||||
# Developer-friendly way to set expires_at
|
||||
field :expires_in, :integer, virtual: true, default: 30
|
||||
|
||||
belongs_to :user, FzHttp.Users.User
|
||||
belongs_to :user, Domain.Users.User
|
||||
|
||||
timestamps(updated_at: false)
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.ApiTokens.ApiToken.Changeset do
|
||||
use FzHttp, :changeset
|
||||
alias FzHttp.ApiTokens.ApiToken
|
||||
defmodule Domain.ApiTokens.ApiToken.Changeset do
|
||||
use Domain, :changeset
|
||||
alias Domain.ApiTokens.ApiToken
|
||||
|
||||
@max_per_user 25
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.ApiTokens.ApiToken.Query do
|
||||
use FzHttp, :query
|
||||
defmodule Domain.ApiTokens.ApiToken.Query do
|
||||
use Domain, :query
|
||||
|
||||
def all do
|
||||
from(api_tokens in FzHttp.ApiTokens.ApiToken, as: :api_tokens)
|
||||
from(api_tokens in Domain.ApiTokens.ApiToken, as: :api_tokens)
|
||||
end
|
||||
|
||||
def by_id(queryable \\ all(), id) do
|
||||
@@ -1,11 +1,11 @@
|
||||
defmodule FzHttp.ApiTokens.Authorizer do
|
||||
use FzHttp.Auth.Authorizer
|
||||
alias FzHttp.ApiTokens.ApiToken
|
||||
defmodule Domain.ApiTokens.Authorizer do
|
||||
use Domain.Auth.Authorizer
|
||||
alias Domain.ApiTokens.ApiToken
|
||||
|
||||
def manage_own_api_tokens_permission, do: build(ApiToken, :manage_own)
|
||||
def manage_api_tokens_permission, do: build(ApiToken, :manage)
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def list_permissions_for_role(:admin) do
|
||||
[
|
||||
manage_own_api_tokens_permission(),
|
||||
@@ -17,7 +17,7 @@ defmodule FzHttp.ApiTokens.Authorizer do
|
||||
[]
|
||||
end
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def for_subject(queryable, %Subject{} = subject) when is_user(subject) do
|
||||
cond do
|
||||
has_permission?(subject, manage_api_tokens_permission()) ->
|
||||
39
apps/domain/lib/domain/application.ex
Normal file
39
apps/domain/lib/domain/application.ex
Normal file
@@ -0,0 +1,39 @@
|
||||
defmodule Domain.Application do
|
||||
use Application
|
||||
|
||||
def start(_type, _args) do
|
||||
result =
|
||||
Supervisor.start_link(children(), strategy: :one_for_one, name: __MODULE__.Supervisor)
|
||||
|
||||
:ok = after_start()
|
||||
result
|
||||
end
|
||||
|
||||
# TODO: when app starts for migrations set env to disable connectivity checks and telemetry
|
||||
def children do
|
||||
[
|
||||
# Infrastructure services
|
||||
Domain.Repo,
|
||||
Domain.Vault,
|
||||
{Phoenix.PubSub, name: Domain.PubSub},
|
||||
|
||||
# Application
|
||||
{Domain.Notifications, name: Domain.Notifications},
|
||||
Domain.Auth,
|
||||
|
||||
# Observability
|
||||
Domain.ConnectivityChecks,
|
||||
Domain.Telemetry
|
||||
]
|
||||
end
|
||||
|
||||
if Mix.env() == :prod do
|
||||
defp after_start do
|
||||
Domain.Config.validate_runtime_config!()
|
||||
end
|
||||
else
|
||||
defp after_start do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
defmodule FzHttp.Auth do
|
||||
defmodule Domain.Auth do
|
||||
use Supervisor
|
||||
alias FzHttp.Repo
|
||||
alias FzHttp.Config
|
||||
alias FzHttp.{Users, ApiTokens}
|
||||
alias FzHttp.Auth.{Subject, Context, Permission, Roles}
|
||||
alias Domain.Repo
|
||||
alias Domain.Config
|
||||
alias Domain.{Users, ApiTokens}
|
||||
alias Domain.Auth.{Subject, Context, Permission, Roles}
|
||||
|
||||
def start_link(opts) do
|
||||
Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
|
||||
@@ -11,9 +11,9 @@ defmodule FzHttp.Auth do
|
||||
|
||||
def init(_opts) do
|
||||
children = [
|
||||
FzHttp.Auth.SAML.StartProxy,
|
||||
{DynamicSupervisor, name: FzHttp.RefresherSupervisor, strategy: :one_for_one},
|
||||
FzHttp.Auth.OIDC.RefreshManager
|
||||
Domain.Auth.SAML.StartProxy,
|
||||
{DynamicSupervisor, name: Domain.RefresherSupervisor, strategy: :one_for_one},
|
||||
Domain.Auth.OIDC.RefreshManager
|
||||
]
|
||||
|
||||
Supervisor.init(children, strategy: :one_for_one)
|
||||
@@ -84,7 +84,7 @@ defmodule FzHttp.Auth do
|
||||
if provider.redirect_uri do
|
||||
provider.redirect_uri
|
||||
else
|
||||
external_url = FzHttp.Config.fetch_env!(:fz_http, :external_url)
|
||||
external_url = Domain.Config.fetch_env!(:web, :external_url)
|
||||
"#{external_url}auth/oidc/#{provider.id}/callback/"
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Auth.Authorizer do
|
||||
defmodule Domain.Auth.Authorizer do
|
||||
@moduledoc """
|
||||
Business contexts use authorization modules to define permissions that are supported by the context
|
||||
and expose them to the authorization system by implementing behaviour provided by this module.
|
||||
@@ -6,11 +6,11 @@ defmodule FzHttp.Auth.Authorizer do
|
||||
|
||||
defmacro __using__(_opts) do
|
||||
quote do
|
||||
import FzHttp.Auth.Authorizer
|
||||
import FzHttp.Auth, only: [has_permission?: 2]
|
||||
alias FzHttp.Auth.Subject
|
||||
import Domain.Auth.Authorizer
|
||||
import Domain.Auth, only: [has_permission?: 2]
|
||||
alias Domain.Auth.Subject
|
||||
|
||||
@behaviour FzHttp.Auth.Authorizer
|
||||
@behaviour Domain.Auth.Authorizer
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18,24 +18,24 @@ defmodule FzHttp.Auth.Authorizer do
|
||||
Returns list of all permissions defined by implementation module,
|
||||
which is used to simplify role management.
|
||||
"""
|
||||
@callback list_permissions_for_role(FzHttp.Auth.Role.name()) :: [FzHttp.Auth.Permission.t()]
|
||||
@callback list_permissions_for_role(Domain.Auth.Role.name()) :: [Domain.Auth.Permission.t()]
|
||||
|
||||
@doc """
|
||||
Optional helper which allows to filter queryable based on subject permissions.
|
||||
"""
|
||||
@callback for_subject(Ecto.Queryable.t(), FzHttp.Auth.Subject.t()) :: Ecto.Queryable.t()
|
||||
@callback for_subject(Ecto.Queryable.t(), Domain.Auth.Subject.t()) :: Ecto.Queryable.t()
|
||||
|
||||
@optional_callbacks for_subject: 2
|
||||
|
||||
def build(resource, action) do
|
||||
%FzHttp.Auth.Permission{resource: resource, action: action}
|
||||
%Domain.Auth.Permission{resource: resource, action: action}
|
||||
end
|
||||
|
||||
defguard is_user(subject)
|
||||
when is_struct(subject, FzHttp.Auth.Subject) and
|
||||
when is_struct(subject, Domain.Auth.Subject) and
|
||||
elem(subject.actor, 0) == :user
|
||||
|
||||
defguard is_api_token(subject)
|
||||
when is_struct(subject, FzHttp.Auth.Subject) and
|
||||
when is_struct(subject, Domain.Auth.Subject) and
|
||||
elem(subject.actor, 0) == :api_token
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Auth.Context do
|
||||
defmodule Domain.Auth.Context do
|
||||
@typedoc """
|
||||
This structure represents an authentication context for a user or an API token.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule FzHttp.Auth.MFA do
|
||||
alias FzHttp.{Repo, Validator}
|
||||
alias FzHttp.Users
|
||||
alias FzHttp.Auth.MFA.Method
|
||||
defmodule Domain.Auth.MFA do
|
||||
alias Domain.{Repo, Validator}
|
||||
alias Domain.Users
|
||||
alias Domain.Auth.MFA.Method
|
||||
|
||||
def count_users_with_mfa_enabled do
|
||||
Method.Query.select_distinct_user_ids_count()
|
||||
@@ -1,15 +1,15 @@
|
||||
defmodule FzHttp.Auth.MFA.Method do
|
||||
use FzHttp, :schema
|
||||
defmodule Domain.Auth.MFA.Method do
|
||||
use Domain, :schema
|
||||
|
||||
schema "mfa_methods" do
|
||||
field :name, :string
|
||||
field :type, Ecto.Enum, values: [:totp, :native, :portable]
|
||||
field :last_used_at, :utc_datetime_usec
|
||||
field :payload, FzHttp.Encrypted.Map
|
||||
field :payload, Domain.Encrypted.Map
|
||||
|
||||
field :code, :string, virtual: true
|
||||
|
||||
belongs_to :user, FzHttp.Users.User
|
||||
belongs_to :user, Domain.Users.User
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.Auth.MFA.Method.Changeset do
|
||||
use FzHttp, :changeset
|
||||
alias FzHttp.Auth.MFA.Method
|
||||
defmodule Domain.Auth.MFA.Method.Changeset do
|
||||
use Domain, :changeset
|
||||
alias Domain.Auth.MFA.Method
|
||||
|
||||
@create_fields [:name, :type, :payload, :code]
|
||||
|
||||
@@ -11,7 +11,7 @@ defmodule FzHttp.Auth.MFA.Method.Changeset do
|
||||
|> validate_length(:name, min: 1, max: 255)
|
||||
|> trim_change(:name)
|
||||
|> unique_constraint(:name, name: :mfa_methods_user_id_name_index)
|
||||
|> unsafe_validate_unique([:name, :user_id], FzHttp.Repo)
|
||||
|> unsafe_validate_unique([:name, :user_id], Domain.Repo)
|
||||
|> assoc_constraint(:user)
|
||||
|> changeset()
|
||||
end
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.Auth.MFA.Method.Query do
|
||||
use FzHttp, :query
|
||||
defmodule Domain.Auth.MFA.Method.Query do
|
||||
use Domain, :query
|
||||
|
||||
def all do
|
||||
from(users in FzHttp.Auth.MFA.Method, as: :methods)
|
||||
from(users in Domain.Auth.MFA.Method, as: :methods)
|
||||
end
|
||||
|
||||
def by_id(queryable \\ all(), id) do
|
||||
@@ -1,9 +1,9 @@
|
||||
defmodule FzHttp.Auth.OIDC do
|
||||
defmodule Domain.Auth.OIDC do
|
||||
@moduledoc """
|
||||
The OIDC context.
|
||||
"""
|
||||
import Ecto.Query, warn: false
|
||||
alias FzHttp.{Auth.OIDC.Connection, Repo, Users.User}
|
||||
alias Domain.{Auth.OIDC.Connection, Repo, Users.User}
|
||||
|
||||
def list_connections(%User{id: id}) do
|
||||
Repo.all(from(Connection, where: [user_id: ^id]))
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.Auth.OIDC.Connection do
|
||||
use FzHttp, :schema
|
||||
defmodule Domain.Auth.OIDC.Connection do
|
||||
use Domain, :schema
|
||||
|
||||
schema "oidc_connections" do
|
||||
field :provider, :string
|
||||
@@ -7,7 +7,7 @@ defmodule FzHttp.Auth.OIDC.Connection do
|
||||
field :refresh_token, :string
|
||||
field :refreshed_at, :utc_datetime_usec
|
||||
|
||||
belongs_to :user, FzHttp.Users.User
|
||||
belongs_to :user, Domain.Users.User
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.Auth.OIDC.Connection.Changeset do
|
||||
use FzHttp, :changeset
|
||||
defmodule Domain.Auth.OIDC.Connection.Changeset do
|
||||
use Domain, :changeset
|
||||
|
||||
@fields ~w[provider refresh_token refreshed_at refresh_response]a
|
||||
@required_fields ~w[provider refresh_token]a
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.Auth.OIDC.Connection.Query do
|
||||
use FzHttp, :query
|
||||
defmodule Domain.Auth.OIDC.Connection.Query do
|
||||
use Domain, :query
|
||||
|
||||
def all do
|
||||
from(connection in FzHttp.Auth.OIDC.Connection, as: :connection)
|
||||
from(connection in Domain.Auth.OIDC.Connection, as: :connection)
|
||||
end
|
||||
|
||||
def by_id(queryable \\ all(), id) do
|
||||
@@ -1,11 +1,11 @@
|
||||
defmodule FzHttp.Auth.OIDC.RefreshManager do
|
||||
defmodule Domain.Auth.OIDC.RefreshManager do
|
||||
@moduledoc """
|
||||
Manager module for refreshing OIDC connections
|
||||
"""
|
||||
use GenServer, restart: :permanent
|
||||
|
||||
import Ecto.Query
|
||||
alias FzHttp.{Repo, Users.User}
|
||||
alias Domain.{Repo, Users.User}
|
||||
|
||||
# Refresh every 10 minutes -- Keycloak's ttl for refresh tokens
|
||||
# is 30 minutes by default.
|
||||
@@ -46,8 +46,8 @@ defmodule FzHttp.Auth.OIDC.RefreshManager do
|
||||
delay_after_spawn = Enum.random(1..@max_delay_after_spawn) * 1000
|
||||
|
||||
DynamicSupervisor.start_child(
|
||||
FzHttp.RefresherSupervisor,
|
||||
{FzHttp.Auth.OIDC.Refresher, {id, delay_after_spawn}}
|
||||
Domain.RefresherSupervisor,
|
||||
{Domain.Auth.OIDC.Refresher, {id, delay_after_spawn}}
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
defmodule FzHttp.Auth.OIDC.Refresher do
|
||||
defmodule Domain.Auth.OIDC.Refresher do
|
||||
@moduledoc """
|
||||
Worker module for refreshing OIDC connections
|
||||
"""
|
||||
use GenServer, restart: :temporary
|
||||
alias FzHttp.{Auth, Auth.OIDC, Auth.OIDC.Connection, Repo, Users}
|
||||
alias Domain.{Auth, Auth.OIDC, Auth.OIDC.Connection, Repo, Users}
|
||||
require Logger
|
||||
|
||||
def start_link(init_opts) do
|
||||
@@ -60,6 +60,6 @@ defmodule FzHttp.Auth.OIDC.Refresher do
|
||||
end
|
||||
|
||||
defp enabled? do
|
||||
FzHttp.Config.fetch_config!(:disable_vpn_on_oidc_error)
|
||||
Domain.Config.fetch_config!(:disable_vpn_on_oidc_error)
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Auth.Permission do
|
||||
defmodule Domain.Auth.Permission do
|
||||
@type resource :: module()
|
||||
|
||||
@typedoc """
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.Auth.Role do
|
||||
alias FzHttp.Auth.Permission
|
||||
defmodule Domain.Auth.Role do
|
||||
alias Domain.Auth.Permission
|
||||
|
||||
@type name :: atom()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.Auth.Roles do
|
||||
alias FzHttp.Auth.Role
|
||||
defmodule Domain.Auth.Roles do
|
||||
alias Domain.Auth.Role
|
||||
|
||||
def list_roles do
|
||||
[
|
||||
@@ -10,12 +10,12 @@ defmodule FzHttp.Auth.Roles do
|
||||
|
||||
defp list_authorizers do
|
||||
[
|
||||
FzHttp.Config.Authorizer,
|
||||
FzHttp.ApiTokens.Authorizer,
|
||||
FzHttp.ConnectivityChecks.Authorizer,
|
||||
FzHttp.Devices.Authorizer,
|
||||
FzHttp.Rules.Authorizer,
|
||||
FzHttp.Users.Authorizer
|
||||
Domain.Config.Authorizer,
|
||||
Domain.ApiTokens.Authorizer,
|
||||
Domain.ConnectivityChecks.Authorizer,
|
||||
Domain.Devices.Authorizer,
|
||||
Domain.Rules.Authorizer,
|
||||
Domain.Users.Authorizer
|
||||
]
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Auth.SAML.StartProxy do
|
||||
defmodule Domain.Auth.SAML.StartProxy do
|
||||
@moduledoc """
|
||||
This proxy starts Samly.Provider with proper configs
|
||||
"""
|
||||
@@ -20,7 +20,7 @@ defmodule FzHttp.Auth.SAML.StartProxy do
|
||||
end
|
||||
|
||||
def set_service_provider(samly_configs) do
|
||||
config = FzHttp.Config.fetch_env!(:fz_http, FzHttp.SAML)
|
||||
config = Domain.Config.fetch_env!(:web, Web.SAML)
|
||||
entity_id = Keyword.fetch!(config, :entity_id)
|
||||
keyfile = Keyword.fetch!(config, :keyfile_path)
|
||||
certfile = Keyword.fetch!(config, :certfile_path)
|
||||
@@ -55,9 +55,9 @@ defmodule FzHttp.Auth.SAML.StartProxy do
|
||||
Keyword.put(samly_configs, :identity_providers, identity_providers)
|
||||
end
|
||||
|
||||
def refresh(providers \\ FzHttp.Config.fetch_config!(:saml_identity_providers)) do
|
||||
def refresh(providers \\ Domain.Config.fetch_config!(:saml_identity_providers)) do
|
||||
samly_configs =
|
||||
FzHttp.Config.fetch_env!(:samly, Samly.Provider)
|
||||
Domain.Config.fetch_env!(:samly, Samly.Provider)
|
||||
|> set_service_provider()
|
||||
|> set_identity_providers(providers)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
defmodule FzHttp.Auth.Subject do
|
||||
alias FzHttp.Auth.{Permission, Context}
|
||||
defmodule Domain.Auth.Subject do
|
||||
alias Domain.Auth.{Permission, Context}
|
||||
|
||||
@type actor ::
|
||||
{:user, %FzHttp.Users.User{}}
|
||||
| {:api_token, %FzHttp.ApiTokens.ApiToken{}}
|
||||
{:user, %Domain.Users.User{}}
|
||||
| {:api_token, %Domain.ApiTokens.ApiToken{}}
|
||||
| :system
|
||||
|
||||
@type permission :: Permission.t()
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.Config do
|
||||
alias FzHttp.{Repo, Auth}
|
||||
alias FzHttp.Config.Authorizer
|
||||
alias FzHttp.Config.{Definition, Definitions, Validator, Errors, Fetcher}
|
||||
alias FzHttp.Config.Configuration
|
||||
defmodule Domain.Config do
|
||||
alias Domain.{Repo, Auth}
|
||||
alias Domain.Config.Authorizer
|
||||
alias Domain.Config.{Definition, Definitions, Validator, Errors, Fetcher}
|
||||
alias Domain.Config.Configuration
|
||||
|
||||
def fetch_source_and_config!(key) do
|
||||
db_config = maybe_fetch_db_config!(key)
|
||||
@@ -59,7 +59,7 @@ defmodule FzHttp.Config do
|
||||
end
|
||||
|
||||
defp maybe_fetch_db_config!(keys) when is_list(keys) do
|
||||
if Enum.any?(keys, &(&1 in FzHttp.Config.Configuration.__schema__(:fields))) do
|
||||
if Enum.any?(keys, &(&1 in Domain.Config.Configuration.__schema__(:fields))) do
|
||||
fetch_db_config!()
|
||||
else
|
||||
%{}
|
||||
@@ -67,7 +67,7 @@ defmodule FzHttp.Config do
|
||||
end
|
||||
|
||||
defp maybe_fetch_db_config!(key) do
|
||||
if key in FzHttp.Config.Configuration.__schema__(:fields) do
|
||||
if key in Domain.Config.Configuration.__schema__(:fields) do
|
||||
fetch_db_config!()
|
||||
else
|
||||
%{}
|
||||
@@ -134,7 +134,7 @@ defmodule FzHttp.Config do
|
||||
changeset = Configuration.Changeset.changeset(config, attrs)
|
||||
|
||||
with {:ok, config} <- Repo.update(changeset) do
|
||||
FzHttp.Auth.SAML.StartProxy.refresh(config.saml_identity_providers)
|
||||
Domain.Auth.SAML.StartProxy.refresh(config.saml_identity_providers)
|
||||
{:ok, config}
|
||||
end
|
||||
end
|
||||
@@ -176,13 +176,13 @@ defmodule FzHttp.Config do
|
||||
if Mix.env() != :test do
|
||||
defdelegate fetch_env!(app, key), to: Application
|
||||
else
|
||||
def put_env_override(app \\ :fz_http, key, value) do
|
||||
def put_env_override(app \\ :domain, key, value) do
|
||||
Process.put(pdict_key_function(app, key), value)
|
||||
:ok
|
||||
end
|
||||
|
||||
def put_system_env_override(key, value) when is_atom(key) do
|
||||
Process.put({FzHttp.Config.Resolver, key}, {:env, value})
|
||||
Process.put({Domain.Config.Resolver, key}, {:env, value})
|
||||
:ok
|
||||
end
|
||||
|
||||
@@ -199,7 +199,7 @@ defmodule FzHttp.Config do
|
||||
application_env = Application.fetch_env!(app, key)
|
||||
|
||||
pdict_key_function(app, key)
|
||||
|> FzHttp.Config.Resolver.fetch_process_env()
|
||||
|> Domain.Config.Resolver.fetch_process_env()
|
||||
|> case do
|
||||
{:ok, override} ->
|
||||
override
|
||||
@@ -1,10 +1,10 @@
|
||||
defmodule FzHttp.Config.Authorizer do
|
||||
use FzHttp.Auth.Authorizer
|
||||
alias FzHttp.Config.Configuration
|
||||
defmodule Domain.Config.Authorizer do
|
||||
use Domain.Auth.Authorizer
|
||||
alias Domain.Config.Configuration
|
||||
|
||||
def configure_permission, do: build(Configuration, :manage)
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def list_permissions_for_role(:admin) do
|
||||
[
|
||||
configure_permission()
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Config.Caster do
|
||||
defmodule Domain.Config.Caster do
|
||||
@moduledoc """
|
||||
This module allows to cast values to a defined type.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.Config.Configuration do
|
||||
use FzHttp, :schema
|
||||
alias FzHttp.Config.Logo
|
||||
defmodule Domain.Config.Configuration do
|
||||
use Domain, :schema
|
||||
alias Domain.Config.Logo
|
||||
|
||||
schema "configurations" do
|
||||
field :allow_unprivileged_device_management, :boolean
|
||||
@@ -10,7 +10,7 @@ defmodule FzHttp.Config.Configuration do
|
||||
field :disable_vpn_on_oidc_error, :boolean
|
||||
|
||||
# The defaults for these fields are set in the following migration:
|
||||
# apps/fz_http/priv/repo/migrations/20221224210654_fix_sites_nullable_fields.exs
|
||||
# apps/domain/priv/repo/migrations/20221224210654_fix_sites_nullable_fields.exs
|
||||
#
|
||||
# This will be changing in 0.8 and again when we have client apps,
|
||||
# so this works for the time being. The important thing is allowing users
|
||||
@@ -22,7 +22,7 @@ defmodule FzHttp.Config.Configuration do
|
||||
field :default_client_mtu, :integer
|
||||
field :default_client_endpoint, :string
|
||||
field :default_client_dns, {:array, :string}, default: []
|
||||
field :default_client_allowed_ips, {:array, FzHttp.Types.INET}, default: []
|
||||
field :default_client_allowed_ips, {:array, Domain.Types.INET}, default: []
|
||||
|
||||
# XXX: Remove when this feature is refactored into config expiration feature
|
||||
# and WireGuard keys are decoupled from devices to facilitate rotation.
|
||||
@@ -33,11 +33,11 @@ defmodule FzHttp.Config.Configuration do
|
||||
embeds_one :logo, Logo, on_replace: :delete
|
||||
|
||||
embeds_many :openid_connect_providers,
|
||||
FzHttp.Config.Configuration.OpenIDConnectProvider,
|
||||
Domain.Config.Configuration.OpenIDConnectProvider,
|
||||
on_replace: :delete
|
||||
|
||||
embeds_many :saml_identity_providers,
|
||||
FzHttp.Config.Configuration.SAMLIdentityProvider,
|
||||
Domain.Config.Configuration.SAMLIdentityProvider,
|
||||
on_replace: :delete
|
||||
|
||||
timestamps()
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.Config.Configuration.Changeset do
|
||||
use FzHttp, :changeset
|
||||
import FzHttp.Config, only: [config_changeset: 2]
|
||||
defmodule Domain.Config.Configuration.Changeset do
|
||||
use Domain, :changeset
|
||||
import Domain.Config, only: [config_changeset: 2]
|
||||
|
||||
# Postgres max int size is 4 bytes
|
||||
@max_vpn_session_duration 2_147_483_647
|
||||
@@ -32,10 +32,10 @@ defmodule FzHttp.Config.Configuration.Changeset do
|
||||
|> cast(attrs, @fields)
|
||||
|> cast_embed(:logo)
|
||||
|> cast_embed(:openid_connect_providers,
|
||||
with: {FzHttp.Config.Configuration.OpenIDConnectProvider, :changeset, []}
|
||||
with: {Domain.Config.Configuration.OpenIDConnectProvider, :changeset, []}
|
||||
)
|
||||
|> cast_embed(:saml_identity_providers,
|
||||
with: {FzHttp.Config.Configuration.SAMLIdentityProvider, :changeset, []}
|
||||
with: {Domain.Config.Configuration.SAMLIdentityProvider, :changeset, []}
|
||||
)
|
||||
|> trim_change(:default_client_dns)
|
||||
|> trim_change(:default_client_endpoint)
|
||||
@@ -48,7 +48,7 @@ defmodule FzHttp.Config.Configuration.Changeset do
|
||||
|
||||
defp ensure_no_overridden_changes(changeset) do
|
||||
changed_keys = Map.keys(changeset.changes)
|
||||
configs = FzHttp.Config.fetch_source_and_configs!(changed_keys)
|
||||
configs = Domain.Config.fetch_source_and_configs!(changed_keys)
|
||||
|
||||
Enum.reduce(changed_keys, changeset, fn key, changeset ->
|
||||
case Map.fetch!(configs, key) do
|
||||
@@ -1,10 +1,10 @@
|
||||
defmodule FzHttp.Config.Configuration.OpenIDConnectProvider do
|
||||
defmodule Domain.Config.Configuration.OpenIDConnectProvider do
|
||||
@moduledoc """
|
||||
OIDC Config virtual schema
|
||||
"""
|
||||
use FzHttp, :schema
|
||||
use Domain, :schema
|
||||
import Ecto.Changeset
|
||||
alias FzHttp.Validator
|
||||
alias Domain.Validator
|
||||
|
||||
@reserved_config_ids [
|
||||
"identity",
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.Config.Configuration.SAMLIdentityProvider do
|
||||
defmodule Domain.Config.Configuration.SAMLIdentityProvider do
|
||||
@moduledoc """
|
||||
SAML Config virtual schema
|
||||
"""
|
||||
use FzHttp, :schema
|
||||
use Domain, :schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@reserved_config_ids [
|
||||
@@ -48,7 +48,7 @@ defmodule FzHttp.Config.Configuration.SAMLIdentityProvider do
|
||||
:metadata,
|
||||
:auto_create_users
|
||||
])
|
||||
|> FzHttp.Validator.validate_uri(:base_url)
|
||||
|> Domain.Validator.validate_uri(:base_url)
|
||||
|> validate_metadata()
|
||||
# Don't allow users to enter reserved config ids
|
||||
|> validate_exclusion(:id, @reserved_config_ids)
|
||||
@@ -69,7 +69,7 @@ defmodule FzHttp.Config.Configuration.SAMLIdentityProvider do
|
||||
|
||||
defp gen_default_base_url(changeset) do
|
||||
default_base_url =
|
||||
FzHttp.Config.fetch_env!(:fz_http, :external_url)
|
||||
Domain.Config.fetch_env!(:web, :external_url)
|
||||
|> Path.join("/auth/saml")
|
||||
|
||||
base_url = get_change(changeset, :base_url, default_base_url)
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Config.Definition do
|
||||
defmodule Domain.Config.Definition do
|
||||
@moduledoc """
|
||||
This module provides a DSL to define application configuration, which can be read from multiple sources,
|
||||
casted and validated.
|
||||
@@ -6,7 +6,7 @@ defmodule FzHttp.Config.Definition do
|
||||
## Examples
|
||||
|
||||
defmodule MyConfig do
|
||||
use FzHttp.Config.Definition
|
||||
use Domain.Config.Definition
|
||||
|
||||
@doc "My config key"
|
||||
defconfig :my_key, :string, required: true
|
||||
@@ -21,7 +21,7 @@ defmodule FzHttp.Config.Definition do
|
||||
iex> MyConfig.fetch_doc(:my_key)
|
||||
{:ok, "My config key"}
|
||||
"""
|
||||
alias FzHttp.Config.Errors
|
||||
alias Domain.Config.Errors
|
||||
|
||||
@type array_opts :: [{:validate_unique, boolean()} | {:validate_length, Keyword.t()}]
|
||||
|
||||
@@ -53,17 +53,17 @@ defmodule FzHttp.Config.Definition do
|
||||
|
||||
defmacro __using__(_opts) do
|
||||
quote do
|
||||
import FzHttp.Config.Definition
|
||||
import FzHttp.Config, only: [compile_config!: 1]
|
||||
import Domain.Config.Definition
|
||||
import Domain.Config, only: [compile_config!: 1]
|
||||
|
||||
# Accumulator keeps the list of defined config keys
|
||||
Module.register_attribute(__MODULE__, :configs, accumulate: true)
|
||||
|
||||
# A `configs/0` function is injected before module is compiled
|
||||
# exporting the aggregated list of config keys
|
||||
@before_compile FzHttp.Config.Definition
|
||||
@before_compile Domain.Config.Definition
|
||||
|
||||
@doc "See `FzHttp.Config.Definition.fetch_doc/2`"
|
||||
@doc "See `Domain.Config.Definition.fetch_doc/2`"
|
||||
def fetch_doc(key), do: fetch_doc(__MODULE__, key)
|
||||
end
|
||||
end
|
||||
@@ -96,7 +96,7 @@ defmodule FzHttp.Config.Definition do
|
||||
defmacro defconfig(key, type, opts \\ []) do
|
||||
quote do
|
||||
@configs {__MODULE__, unquote(key)}
|
||||
@spec unquote(key)() :: {FzHttp.Config.Definition.type(), FzHttp.Config.Definition.opts()}
|
||||
@spec unquote(key)() :: {Domain.Config.Definition.type(), Domain.Config.Definition.opts()}
|
||||
def unquote(key)(), do: {unquote(type), unquote(opts)}
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,5 @@
|
||||
defmodule FzHttp.Config.Definitions do
|
||||
# TODO: clean up unused definitions
|
||||
defmodule Domain.Config.Definitions do
|
||||
@moduledoc """
|
||||
Most day-to-day config of Firezone can be done via the Firezone Web UI,
|
||||
but for zero-touch deployments we allow to override most of configuration options
|
||||
@@ -27,10 +28,10 @@ defmodule FzHttp.Config.Definitions do
|
||||
It means that if environment variable is set, it will be used, regardless of the database value,
|
||||
and UI to edit database value will be disabled.
|
||||
"""
|
||||
use FzHttp.Config.Definition
|
||||
alias FzHttp.Config.Dumper
|
||||
alias FzHttp.Types
|
||||
alias FzHttp.Config.{Configuration, Logo}
|
||||
use Domain.Config.Definition
|
||||
alias Domain.Config.Dumper
|
||||
alias Domain.Types
|
||||
alias Domain.Config.{Configuration, Logo}
|
||||
|
||||
def doc_sections do
|
||||
[
|
||||
@@ -156,8 +157,8 @@ defmodule FzHttp.Config.Definitions do
|
||||
defconfig(:external_url, :string,
|
||||
changeset: fn changeset, key ->
|
||||
changeset
|
||||
|> FzHttp.Validator.validate_uri(key, require_trailing_slash: true)
|
||||
|> FzHttp.Validator.normalize_url(key)
|
||||
|> Domain.Validator.validate_uri(key, require_trailing_slash: true)
|
||||
|> Domain.Validator.normalize_url(key)
|
||||
end
|
||||
)
|
||||
|
||||
@@ -289,7 +290,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
)
|
||||
|
||||
defconfig(:database_parameters, :map,
|
||||
default: %{application_name: "firezone-#{Application.spec(:fz_http, :vsn)}"},
|
||||
default: %{application_name: "firezone-#{Application.spec(:domain, :vsn)}"},
|
||||
dump: &Dumper.keyword/1
|
||||
)
|
||||
|
||||
@@ -314,8 +315,8 @@ defmodule FzHttp.Config.Definitions do
|
||||
legacy_keys: [{:env, "ADMIN_EMAIL", "0.9"}],
|
||||
changeset: fn changeset, key ->
|
||||
changeset
|
||||
|> FzHttp.Validator.trim_change(key)
|
||||
|> FzHttp.Validator.validate_email(key)
|
||||
|> Domain.Validator.trim_change(key)
|
||||
|> Domain.Validator.validate_email(key)
|
||||
end
|
||||
)
|
||||
|
||||
@@ -339,7 +340,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
"""
|
||||
defconfig(:guardian_secret_key, :string,
|
||||
sensitive: true,
|
||||
changeset: &FzHttp.Validator.validate_base64/2
|
||||
changeset: &Domain.Validator.validate_base64/2
|
||||
)
|
||||
|
||||
@doc """
|
||||
@@ -347,7 +348,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
"""
|
||||
defconfig(:database_encryption_key, :string,
|
||||
sensitive: true,
|
||||
changeset: &FzHttp.Validator.validate_base64/2
|
||||
changeset: &Domain.Validator.validate_base64/2
|
||||
)
|
||||
|
||||
@doc """
|
||||
@@ -355,7 +356,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
"""
|
||||
defconfig(:secret_key_base, :string,
|
||||
sensitive: true,
|
||||
changeset: &FzHttp.Validator.validate_base64/2
|
||||
changeset: &Domain.Validator.validate_base64/2
|
||||
)
|
||||
|
||||
@doc """
|
||||
@@ -363,7 +364,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
"""
|
||||
defconfig(:live_view_signing_salt, :string,
|
||||
sensitive: true,
|
||||
changeset: &FzHttp.Validator.validate_base64/2
|
||||
changeset: &Domain.Validator.validate_base64/2
|
||||
)
|
||||
|
||||
@doc """
|
||||
@@ -371,7 +372,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
"""
|
||||
defconfig(:cookie_signing_salt, :string,
|
||||
sensitive: true,
|
||||
changeset: &FzHttp.Validator.validate_base64/2
|
||||
changeset: &Domain.Validator.validate_base64/2
|
||||
)
|
||||
|
||||
@doc """
|
||||
@@ -379,7 +380,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
"""
|
||||
defconfig(:cookie_encryption_salt, :string,
|
||||
sensitive: true,
|
||||
changeset: &FzHttp.Validator.validate_base64/2
|
||||
changeset: &Domain.Validator.validate_base64/2
|
||||
)
|
||||
|
||||
##############################################
|
||||
@@ -455,8 +456,8 @@ defmodule FzHttp.Config.Definitions do
|
||||
|
||||
:string, changeset, key ->
|
||||
changeset
|
||||
|> FzHttp.Validator.trim_change(key)
|
||||
|> FzHttp.Validator.validate_fqdn(key, allow_port: true)
|
||||
|> Domain.Validator.trim_change(key)
|
||||
|> Domain.Validator.validate_fqdn(key, allow_port: true)
|
||||
end
|
||||
)
|
||||
|
||||
@@ -477,8 +478,8 @@ defmodule FzHttp.Config.Definitions do
|
||||
|
||||
:string, changeset, key ->
|
||||
changeset
|
||||
|> FzHttp.Validator.trim_change(key)
|
||||
|> FzHttp.Validator.validate_fqdn(key)
|
||||
|> Domain.Validator.trim_change(key)
|
||||
|> Domain.Validator.validate_fqdn(key)
|
||||
end
|
||||
)
|
||||
|
||||
@@ -537,7 +538,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
"""
|
||||
defconfig(:saml_keyfile_path, :string,
|
||||
default: "/var/firezone/saml.key",
|
||||
changeset: &FzHttp.Validator.validate_file(&1, &2, extensions: ~w[.pem .key])
|
||||
changeset: &Domain.Validator.validate_file(&1, &2, extensions: ~w[.pem .key])
|
||||
)
|
||||
|
||||
@doc """
|
||||
@@ -546,7 +547,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
"""
|
||||
defconfig(:saml_certfile_path, :string,
|
||||
default: "/var/firezone/saml.crt",
|
||||
changeset: &FzHttp.Validator.validate_file(&1, &2, extensions: ~w[.crt .pem])
|
||||
changeset: &Domain.Validator.validate_file(&1, &2, extensions: ~w[.crt .pem])
|
||||
)
|
||||
|
||||
@doc """
|
||||
@@ -673,12 +674,12 @@ defmodule FzHttp.Config.Definitions do
|
||||
|
||||
defconfig(:wireguard_ipv4_network, Types.CIDR,
|
||||
default: "10.3.2.0/24",
|
||||
changeset: &FzHttp.Validator.validate_ip_type_inclusion(&1, &2, [:ipv4])
|
||||
changeset: &Domain.Validator.validate_ip_type_inclusion(&1, &2, [:ipv4])
|
||||
)
|
||||
|
||||
defconfig(:wireguard_ipv4_address, Types.IP,
|
||||
default: "10.3.2.1",
|
||||
changeset: &FzHttp.Validator.validate_ip_type_inclusion(&1, &2, [:ipv4])
|
||||
changeset: &Domain.Validator.validate_ip_type_inclusion(&1, &2, [:ipv4])
|
||||
)
|
||||
|
||||
@doc """
|
||||
@@ -689,19 +690,19 @@ defmodule FzHttp.Config.Definitions do
|
||||
|
||||
defconfig(:wireguard_ipv6_network, Types.CIDR,
|
||||
default: "fd00::3:2:0/120",
|
||||
changeset: &FzHttp.Validator.validate_ip_type_inclusion(&1, &2, [:ipv6])
|
||||
changeset: &Domain.Validator.validate_ip_type_inclusion(&1, &2, [:ipv6])
|
||||
)
|
||||
|
||||
defconfig(:wireguard_ipv6_address, Types.IP,
|
||||
default: "fd00::3:2:1",
|
||||
changeset: &FzHttp.Validator.validate_ip_type_inclusion(&1, &2, [:ipv6])
|
||||
changeset: &Domain.Validator.validate_ip_type_inclusion(&1, &2, [:ipv6])
|
||||
)
|
||||
|
||||
defconfig(:wireguard_private_key_path, :string,
|
||||
default: "/var/firezone/private_key"
|
||||
# We don't check if the file exists, because it is generated on
|
||||
# the first boot.
|
||||
# changeset: &FzHttp.Validator.validate_file(&1, &2)
|
||||
# changeset: &Domain.Validator.validate_file(&1, &2)
|
||||
)
|
||||
|
||||
defconfig(:wireguard_interface_name, :string, default: "wg-firezone")
|
||||
@@ -737,8 +738,8 @@ defmodule FzHttp.Config.Definitions do
|
||||
sensitive: true,
|
||||
changeset: fn changeset, key ->
|
||||
changeset
|
||||
|> FzHttp.Validator.trim_change(key)
|
||||
|> FzHttp.Validator.validate_email(key)
|
||||
|> Domain.Validator.trim_change(key)
|
||||
|> Domain.Validator.validate_email(key)
|
||||
end
|
||||
)
|
||||
|
||||
@@ -768,7 +769,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
Swoosh.Adapters.Sendmail,
|
||||
Swoosh.Adapters.SocketLabs,
|
||||
Swoosh.Adapters.SparkPost,
|
||||
FzHttpWeb.Mailer.NoopAdapter,
|
||||
Web.Mailer.NoopAdapter,
|
||||
# DEPRECATED: Legacy options should be removed in 0.8
|
||||
:smtp,
|
||||
:mailgun,
|
||||
@@ -778,7 +779,7 @@ defmodule FzHttp.Config.Definitions do
|
||||
:sendmail
|
||||
]
|
||||
)},
|
||||
default: FzHttpWeb.Mailer.NoopAdapter,
|
||||
default: Web.Mailer.NoopAdapter,
|
||||
legacy_keys: [{:env, "OUTBOUND_EMAIL_PROVIDER", "0.9"}],
|
||||
dump: fn
|
||||
:smtp -> Swoosh.Adapters.SMTP
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Config.Dumper do
|
||||
defmodule Domain.Config.Dumper do
|
||||
@doc ~S"""
|
||||
Maps JSON-decoded ssl opts to pass to Erlang's ssl module. Most users
|
||||
don't need to override many, if any, SSL opts. Most commonly this is
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.Config.Errors do
|
||||
alias FzHttp.Config.Definition
|
||||
defmodule Domain.Config.Errors do
|
||||
alias Domain.Config.Definition
|
||||
require Logger
|
||||
|
||||
@env_doc_url "https://www.firezone.dev/docs/reference/env-vars/#environment-variable-listing"
|
||||
@@ -48,7 +48,7 @@ defmodule FzHttp.Config.Errors do
|
||||
end
|
||||
|
||||
defp source({:app_env, key}), do: "application environment #{key}"
|
||||
defp source({:env, key}), do: "environment variable #{FzHttp.Config.Resolver.env_key(key)}"
|
||||
defp source({:env, key}), do: "environment variable #{Domain.Config.Resolver.env_key(key)}"
|
||||
defp source({:db, key}), do: "database configuration #{key}"
|
||||
defp source(:default), do: "default value"
|
||||
|
||||
@@ -93,7 +93,7 @@ defmodule FzHttp.Config.Errors do
|
||||
def legacy_key_used(key, legacy_key, removed_at) do
|
||||
Logger.warn(
|
||||
"A legacy configuration option '#{legacy_key}' is used and it will be removed in v#{removed_at}. " <>
|
||||
"Please use '#{FzHttp.Config.Resolver.env_key(key)}' configuration option instead."
|
||||
"Please use '#{Domain.Config.Resolver.env_key(key)}' configuration option instead."
|
||||
)
|
||||
end
|
||||
|
||||
@@ -107,12 +107,12 @@ defmodule FzHttp.Config.Errors do
|
||||
|
||||
You can set this configuration via environment variable by adding it to `.env` file:
|
||||
|
||||
#{FzHttp.Config.Resolver.env_key(key)}=YOUR_VALUE
|
||||
#{Domain.Config.Resolver.env_key(key)}=YOUR_VALUE
|
||||
"""
|
||||
end
|
||||
|
||||
defp db_example(key) do
|
||||
if key in FzHttp.Config.Configuration.__schema__(:fields) do
|
||||
if key in Domain.Config.Configuration.__schema__(:fields) do
|
||||
"""
|
||||
### Using database
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.Config.Fetcher do
|
||||
alias FzHttp.Config.{Definition, Resolver, Caster, Validator}
|
||||
defmodule Domain.Config.Fetcher do
|
||||
alias Domain.Config.{Definition, Resolver, Caster, Validator}
|
||||
|
||||
@spec fetch_source_and_config(
|
||||
module(),
|
||||
@@ -1,9 +1,9 @@
|
||||
defmodule FzHttp.Config.Logo do
|
||||
defmodule Domain.Config.Logo do
|
||||
@moduledoc """
|
||||
Embedded Schema for logo
|
||||
"""
|
||||
use FzHttp, :schema
|
||||
import FzHttp.Validator
|
||||
use Domain, :schema
|
||||
import Domain.Validator
|
||||
import Ecto.Changeset
|
||||
|
||||
@whitelisted_file_extensions ~w[.jpg .jpeg .png .gif .webp .avif .svg .tiff]
|
||||
@@ -34,7 +34,7 @@ defmodule FzHttp.Config.Logo do
|
||||
defp move_file_to_static(changeset) do
|
||||
case fetch_change(changeset, :file) do
|
||||
{:ok, file} ->
|
||||
directory = Path.join(Application.app_dir(:fz_http), "priv/static/uploads/logo")
|
||||
directory = Path.join(Application.app_dir(:domain), "priv/static/uploads/logo")
|
||||
file_name = Path.basename(file)
|
||||
file_path = Path.join(directory, file_name)
|
||||
File.mkdir_p!(directory)
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.Config.Resolver do
|
||||
alias FzHttp.Config.Errors
|
||||
defmodule Domain.Config.Resolver do
|
||||
alias Domain.Config.Errors
|
||||
|
||||
@type source :: {:env, atom()} | {:db, atom()} | :default
|
||||
|
||||
@@ -7,7 +7,7 @@ defmodule FzHttp.Config.Resolver do
|
||||
key :: atom(),
|
||||
env_configurations :: map(),
|
||||
db_configurations :: map(),
|
||||
opts :: [{:legacy_keys, [FzHttp.Config.Definition.legacy_key()]}]
|
||||
opts :: [{:legacy_keys, [Domain.Config.Definition.legacy_key()]}]
|
||||
) ::
|
||||
{:ok, {source :: source(), value :: term()}} | :error
|
||||
def resolve(key, env_configurations, db_configurations, opts) do
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Config.Validator do
|
||||
defmodule Domain.Config.Validator do
|
||||
import Ecto.Changeset
|
||||
|
||||
def validate(_key, nil, _type, _opts) do
|
||||
@@ -1,11 +1,11 @@
|
||||
defmodule FzHttp.ConnectivityChecks do
|
||||
defmodule Domain.ConnectivityChecks do
|
||||
@moduledoc """
|
||||
The ConnectivityChecks context.
|
||||
"""
|
||||
use Supervisor
|
||||
alias FzHttp.Repo
|
||||
alias FzHttp.Auth
|
||||
alias FzHttp.ConnectivityChecks.{Poller, ConnectivityCheck, Authorizer}
|
||||
alias Domain.Repo
|
||||
alias Domain.Auth
|
||||
alias Domain.ConnectivityChecks.{Poller, ConnectivityCheck, Authorizer}
|
||||
|
||||
@http_client_process_name __MODULE__.Finch
|
||||
|
||||
@@ -14,12 +14,12 @@ defmodule FzHttp.ConnectivityChecks do
|
||||
end
|
||||
|
||||
def init(_opts) do
|
||||
config = FzHttp.Config.fetch_env!(:fz_http, FzHttp.ConnectivityChecks)
|
||||
config = Domain.Config.fetch_env!(:domain, Domain.ConnectivityChecks)
|
||||
transport_opts = Keyword.fetch!(config, :http_client_options)
|
||||
|
||||
children =
|
||||
if Keyword.fetch!(config, :enabled) == true do
|
||||
application_version = Application.spec(:fz_http, :vsn) |> to_string()
|
||||
application_version = Application.spec(:domain, :vsn) |> to_string()
|
||||
connectivity_checks_url = Keyword.fetch!(config, :url)
|
||||
|
||||
request = Finch.build(:get, connectivity_checks_url <> application_version)
|
||||
@@ -1,10 +1,10 @@
|
||||
defmodule FzHttp.ConnectivityChecks.Authorizer do
|
||||
use FzHttp.Auth.Authorizer
|
||||
alias FzHttp.ConnectivityChecks.ConnectivityCheck
|
||||
defmodule Domain.ConnectivityChecks.Authorizer do
|
||||
use Domain.Auth.Authorizer
|
||||
alias Domain.ConnectivityChecks.ConnectivityCheck
|
||||
|
||||
def view_connectivity_checks_permission, do: build(ConnectivityCheck, :view)
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def list_permissions_for_role(:admin) do
|
||||
[
|
||||
view_connectivity_checks_permission()
|
||||
@@ -15,7 +15,7 @@ defmodule FzHttp.ConnectivityChecks.Authorizer do
|
||||
[]
|
||||
end
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def for_subject(queryable, %Subject{} = subject) when is_user(subject) do
|
||||
queryable
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.ConnectivityChecks.ConnectivityCheck do
|
||||
use FzHttp, :schema
|
||||
defmodule Domain.ConnectivityChecks.ConnectivityCheck do
|
||||
use Domain, :schema
|
||||
|
||||
schema "connectivity_checks" do
|
||||
field :response_body, :string
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.ConnectivityChecks.ConnectivityCheck.Changeset do
|
||||
use FzHttp, :changeset
|
||||
alias FzHttp.ConnectivityChecks.ConnectivityCheck
|
||||
defmodule Domain.ConnectivityChecks.ConnectivityCheck.Changeset do
|
||||
use Domain, :changeset
|
||||
alias Domain.ConnectivityChecks.ConnectivityCheck
|
||||
|
||||
def create_changeset(attrs) do
|
||||
%ConnectivityCheck{}
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.ConnectivityChecks.ConnectivityCheck.Query do
|
||||
use FzHttp, :query
|
||||
defmodule Domain.ConnectivityChecks.ConnectivityCheck.Query do
|
||||
use Domain, :query
|
||||
|
||||
def all do
|
||||
from(users in FzHttp.ConnectivityChecks.ConnectivityCheck, as: :connectivity_checks)
|
||||
from(users in Domain.ConnectivityChecks.ConnectivityCheck, as: :connectivity_checks)
|
||||
end
|
||||
|
||||
def by_id(queryable \\ all(), id) do
|
||||
@@ -1,10 +1,10 @@
|
||||
defmodule FzHttp.ConnectivityChecks.Poller do
|
||||
defmodule Domain.ConnectivityChecks.Poller do
|
||||
@moduledoc """
|
||||
A simple GenServer to periodically check for WAN connectivity by issuing
|
||||
POSTs to https://ping[-dev].firez.one/{version}.
|
||||
"""
|
||||
use GenServer
|
||||
alias FzHttp.ConnectivityChecks
|
||||
alias Domain.ConnectivityChecks
|
||||
require Logger
|
||||
|
||||
# Wait a minute before sending the first ping to avoid event spamming when
|
||||
@@ -23,7 +23,7 @@ defmodule FzHttp.ConnectivityChecks.Poller do
|
||||
|
||||
@impl GenServer
|
||||
def handle_info(:start_interval, %{request: request} = state) do
|
||||
FzHttp.Config.fetch_env!(:fz_http, ConnectivityChecks)
|
||||
Domain.Config.fetch_env!(:domain, ConnectivityChecks)
|
||||
|> Keyword.fetch!(:interval)
|
||||
|> :timer.seconds()
|
||||
|> :timer.send_interval(:tick)
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Crypto do
|
||||
defmodule Domain.Crypto do
|
||||
@wg_psk_length 32
|
||||
|
||||
def psk do
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule FzHttp.Devices do
|
||||
alias FzHttp.{Repo, Config, Auth, Validator}
|
||||
alias FzHttp.{Users, Telemetry}
|
||||
alias FzHttp.Devices.{Device, Authorizer}
|
||||
defmodule Domain.Devices do
|
||||
alias Domain.{Repo, Config, Auth, Validator}
|
||||
alias Domain.{Users, Telemetry}
|
||||
alias Domain.Devices.{Device, Authorizer}
|
||||
|
||||
def count do
|
||||
Device.Query.all()
|
||||
@@ -156,7 +156,7 @@ defmodule FzHttp.Devices do
|
||||
end
|
||||
end
|
||||
|
||||
def generate_name(name \\ FzHttp.NameGenerator.generate()) do
|
||||
def generate_name(name \\ Domain.NameGenerator.generate()) do
|
||||
hash =
|
||||
name
|
||||
|> :erlang.phash2(2 ** 16)
|
||||
@@ -200,14 +200,14 @@ defmodule FzHttp.Devices do
|
||||
|
||||
def inet(device) do
|
||||
ips =
|
||||
if Config.fetch_env!(:fz_http, :wireguard_ipv6_enabled) == true do
|
||||
if Config.fetch_env!(:domain, :wireguard_ipv6_enabled) == true do
|
||||
["#{device.ipv6}/128"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
ips =
|
||||
if Config.fetch_env!(:fz_http, :wireguard_ipv4_enabled) == true do
|
||||
if Config.fetch_env!(:domain, :wireguard_ipv4_enabled) == true do
|
||||
["#{device.ipv4}/32"] ++ ips
|
||||
else
|
||||
ips
|
||||
@@ -1,13 +1,13 @@
|
||||
defmodule FzHttp.Devices.Authorizer do
|
||||
use FzHttp.Auth.Authorizer
|
||||
alias FzHttp.Devices.Device
|
||||
defmodule Domain.Devices.Authorizer do
|
||||
use Domain.Auth.Authorizer
|
||||
alias Domain.Devices.Device
|
||||
|
||||
def view_own_devices_permission, do: build(Device, :view_own)
|
||||
def manage_own_devices_permission, do: build(Device, :manage_own)
|
||||
def manage_devices_permission, do: build(Device, :manage)
|
||||
def configure_devices_permission, do: build(Device, :configure)
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
|
||||
def list_permissions_for_role(:admin) do
|
||||
[
|
||||
@@ -23,11 +23,11 @@ defmodule FzHttp.Devices.Authorizer do
|
||||
view_own_devices_permission()
|
||||
]
|
||||
|> add_permission_if(
|
||||
FzHttp.Config.fetch_config!(:allow_unprivileged_device_management),
|
||||
Domain.Config.fetch_config!(:allow_unprivileged_device_management),
|
||||
manage_own_devices_permission()
|
||||
)
|
||||
|> add_permission_if(
|
||||
FzHttp.Config.fetch_config!(:allow_unprivileged_device_configuration),
|
||||
Domain.Config.fetch_config!(:allow_unprivileged_device_configuration),
|
||||
configure_devices_permission()
|
||||
)
|
||||
end
|
||||
@@ -39,7 +39,7 @@ defmodule FzHttp.Devices.Authorizer do
|
||||
defp add_permission_if(permissions, true, permission), do: permissions ++ [permission]
|
||||
defp add_permission_if(permissions, false, _permission), do: permissions
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def for_subject(queryable, %Subject{} = subject) when is_user(subject) do
|
||||
cond do
|
||||
has_permission?(subject, manage_devices_permission()) ->
|
||||
@@ -1,12 +1,12 @@
|
||||
defmodule FzHttp.Devices.Device do
|
||||
use FzHttp, :schema
|
||||
defmodule Domain.Devices.Device do
|
||||
use Domain, :schema
|
||||
|
||||
schema "devices" do
|
||||
field(:name, :string)
|
||||
field(:description, :string)
|
||||
|
||||
field(:public_key, :string)
|
||||
field(:preshared_key, FzHttp.Encrypted.Binary)
|
||||
field(:preshared_key, Domain.Encrypted.Binary)
|
||||
|
||||
field(:use_default_allowed_ips, :boolean, read_after_writes: true, default: true)
|
||||
field(:use_default_dns, :boolean, read_after_writes: true, default: true)
|
||||
@@ -17,18 +17,18 @@ defmodule FzHttp.Devices.Device do
|
||||
field(:endpoint, :string)
|
||||
field(:mtu, :integer)
|
||||
field(:persistent_keepalive, :integer)
|
||||
field(:allowed_ips, {:array, FzHttp.Types.INET}, default: [])
|
||||
field(:allowed_ips, {:array, Domain.Types.INET}, default: [])
|
||||
field(:dns, {:array, :string}, default: [])
|
||||
|
||||
field(:ipv4, FzHttp.Types.IP)
|
||||
field(:ipv6, FzHttp.Types.IP)
|
||||
field(:ipv4, Domain.Types.IP)
|
||||
field(:ipv6, Domain.Types.IP)
|
||||
|
||||
field(:remote_ip, FzHttp.Types.IP)
|
||||
field(:remote_ip, Domain.Types.IP)
|
||||
field(:rx_bytes, :integer)
|
||||
field(:tx_bytes, :integer)
|
||||
field(:latest_handshake, :utc_datetime_usec)
|
||||
|
||||
belongs_to(:user, FzHttp.Users.User)
|
||||
belongs_to(:user, Domain.Users.User)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.Devices.Device.Changeset do
|
||||
use FzHttp, :changeset
|
||||
import FzHttp.Config, only: [config_changeset: 3]
|
||||
alias FzHttp.Users
|
||||
alias FzHttp.Devices
|
||||
defmodule Domain.Devices.Device.Changeset do
|
||||
use Domain, :changeset
|
||||
import Domain.Config, only: [config_changeset: 3]
|
||||
alias Domain.Users
|
||||
alias Domain.Devices
|
||||
|
||||
@create_fields ~w[name description
|
||||
public_key preshared_key]a
|
||||
@@ -34,8 +34,8 @@ defmodule FzHttp.Devices.Device.Changeset do
|
||||
def create_changeset(attrs) do
|
||||
%Devices.Device{}
|
||||
|> cast(attrs, @create_fields)
|
||||
|> put_default_value(:name, &FzHttp.Devices.generate_name/0)
|
||||
|> put_default_value(:preshared_key, &FzHttp.Crypto.psk/0)
|
||||
|> put_default_value(:name, &Domain.Devices.generate_name/0)
|
||||
|> put_default_value(:preshared_key, &Domain.Crypto.psk/0)
|
||||
|> changeset()
|
||||
|> validate_base64(:public_key)
|
||||
|> validate_base64(:preshared_key)
|
||||
@@ -99,7 +99,7 @@ defmodule FzHttp.Devices.Device.Changeset do
|
||||
end
|
||||
|
||||
defp maybe_put_default_ip(changeset, field) do
|
||||
if FzHttp.Config.fetch_env!(:fz_http, :"wireguard_#{field}_enabled") == true do
|
||||
if Domain.Config.fetch_env!(:domain, :"wireguard_#{field}_enabled") == true do
|
||||
case fetch_field(changeset, field) do
|
||||
{:data, nil} -> put_default_ip(changeset, field)
|
||||
:error -> put_default_ip(changeset, field)
|
||||
@@ -113,15 +113,15 @@ defmodule FzHttp.Devices.Device.Changeset do
|
||||
|
||||
defp put_default_ip(changeset, field) do
|
||||
cidr = wireguard_network(field)
|
||||
hosts = FzHttp.Types.CIDR.count_hosts(cidr)
|
||||
hosts = Domain.Types.CIDR.count_hosts(cidr)
|
||||
offset = Enum.random(2..(hosts - 2))
|
||||
|
||||
{:ok, gateway_address} =
|
||||
FzHttp.Config.fetch_env!(:fz_http, :"wireguard_#{field}_address")
|
||||
|> FzHttp.Types.IP.cast()
|
||||
Domain.Config.fetch_env!(:domain, :"wireguard_#{field}_address")
|
||||
|> Domain.Types.IP.cast()
|
||||
|
||||
Devices.Device.Query.next_available_address(cidr, offset, [gateway_address])
|
||||
|> FzHttp.Repo.one()
|
||||
|> Domain.Repo.one()
|
||||
|> case do
|
||||
nil -> add_error(changeset, :base, "CIDR #{cidr} is exhausted")
|
||||
ip -> put_change(changeset, field, ip)
|
||||
@@ -129,7 +129,7 @@ defmodule FzHttp.Devices.Device.Changeset do
|
||||
end
|
||||
|
||||
defp wireguard_network(field) do
|
||||
cidr = FzHttp.Config.fetch_env!(:fz_http, :"wireguard_#{field}_network")
|
||||
cidr = Domain.Config.fetch_env!(:domain, :"wireguard_#{field}_network")
|
||||
%{cidr | netmask: limit_cidr_netmask(field, cidr.netmask)}
|
||||
end
|
||||
|
||||
@@ -137,13 +137,13 @@ defmodule FzHttp.Devices.Device.Changeset do
|
||||
defp limit_cidr_netmask(:ipv6, network), do: max(network, 70)
|
||||
|
||||
defp ipv4_address do
|
||||
FzHttp.Config.fetch_env!(:fz_http, :wireguard_ipv4_address)
|
||||
|> FzHttp.Types.IP.cast()
|
||||
Domain.Config.fetch_env!(:domain, :wireguard_ipv4_address)
|
||||
|> Domain.Types.IP.cast()
|
||||
end
|
||||
|
||||
defp ipv6_address do
|
||||
FzHttp.Config.fetch_env!(:fz_http, :wireguard_ipv6_address)
|
||||
|> FzHttp.Types.IP.cast()
|
||||
Domain.Config.fetch_env!(:domain, :wireguard_ipv6_address)
|
||||
|> Domain.Types.IP.cast()
|
||||
end
|
||||
|
||||
defp validate_max_devices(changeset, user) do
|
||||
@@ -151,7 +151,7 @@ defmodule FzHttp.Devices.Device.Changeset do
|
||||
# At the moment it's not a big concern. Fixing it would require locking against INSERTs or DELETEs
|
||||
# while counts are happening.
|
||||
count = Devices.count_by_user_id(user.id)
|
||||
max_devices = FzHttp.Config.fetch_env!(:fz_http, :max_devices_per_user)
|
||||
max_devices = Domain.Config.fetch_env!(:domain, :max_devices_per_user)
|
||||
|
||||
if count >= max_devices do
|
||||
add_error(
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.Devices.Device.Query do
|
||||
use FzHttp, :query
|
||||
defmodule Domain.Devices.Device.Query do
|
||||
use Domain, :query
|
||||
|
||||
def all do
|
||||
from(devices in FzHttp.Devices.Device, as: :devices)
|
||||
from(devices in Domain.Devices.Device, as: :devices)
|
||||
end
|
||||
|
||||
def by_id(queryable \\ all(), id) do
|
||||
@@ -27,8 +27,8 @@ defmodule FzHttp.Devices.Device.Query do
|
||||
|
||||
def only_active(queryable \\ all()) do
|
||||
dynamic =
|
||||
if FzHttp.Config.vpn_sessions_expire?() do
|
||||
vpn_session_duration = FzHttp.Config.fetch_config!(:vpn_session_duration)
|
||||
if Domain.Config.vpn_sessions_expire?() do
|
||||
vpn_session_duration = Domain.Config.fetch_config!(:vpn_session_duration)
|
||||
|
||||
dynamic(
|
||||
[user: user],
|
||||
@@ -1,9 +1,9 @@
|
||||
defmodule FzHttp.Devices.StatsUpdater do
|
||||
defmodule Domain.Devices.StatsUpdater do
|
||||
@moduledoc """
|
||||
Extracts WireGuard data about each peer and adds it to
|
||||
the correspond device.
|
||||
"""
|
||||
alias FzHttp.{Devices, Devices.Device, Repo}
|
||||
alias Domain.{Devices, Devices.Device, Repo}
|
||||
|
||||
def update(stats) do
|
||||
for {public_key, data} <- stats do
|
||||
7
apps/domain/lib/domain/encrypted/binary.ex
Normal file
7
apps/domain/lib/domain/encrypted/binary.ex
Normal file
@@ -0,0 +1,7 @@
|
||||
defmodule Domain.Encrypted.Binary do
|
||||
@moduledoc """
|
||||
Configures how to encrpyt Binaries to the DB.
|
||||
"""
|
||||
|
||||
use Cloak.Ecto.Binary, vault: Domain.Vault
|
||||
end
|
||||
7
apps/domain/lib/domain/encrypted/map.ex
Normal file
7
apps/domain/lib/domain/encrypted/map.ex
Normal file
@@ -0,0 +1,7 @@
|
||||
defmodule Domain.Encrypted.Map do
|
||||
@moduledoc """
|
||||
Configures how to encrpyt Maps to the DB.
|
||||
"""
|
||||
|
||||
use Cloak.Ecto.Map, vault: Domain.Vault
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.NameGenerator do
|
||||
defmodule Domain.NameGenerator do
|
||||
@adjectives ~w(
|
||||
abandoned able absolute adorable adventurous academic acceptable acclaimed accomplished
|
||||
accurate aching acidic acrobatic active actual adept admirable admired adolescent adorable
|
||||
97
apps/domain/lib/domain/notifications.ex
Normal file
97
apps/domain/lib/domain/notifications.ex
Normal file
@@ -0,0 +1,97 @@
|
||||
defmodule Domain.Notifications do
|
||||
@moduledoc """
|
||||
Notification notifications for notifications live view.
|
||||
"""
|
||||
use GenServer
|
||||
alias Phoenix.PubSub
|
||||
|
||||
@topic "notifications_live"
|
||||
|
||||
def start_link(opts \\ []) do
|
||||
GenServer.start_link(__MODULE__, [], opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a list of current notifications.
|
||||
"""
|
||||
def current, do: current(__MODULE__)
|
||||
def current(nil), do: current()
|
||||
def current(pid), do: GenServer.call(pid, :current)
|
||||
|
||||
@doc """
|
||||
Add a notification.
|
||||
"""
|
||||
def add(notification), do: add(__MODULE__, notification)
|
||||
def add(nil, notification), do: add(notification)
|
||||
def add(pid, notification), do: GenServer.call(pid, {:add, notification})
|
||||
|
||||
@doc """
|
||||
Clear all notifications.
|
||||
"""
|
||||
def clear_all, do: clear_all(__MODULE__)
|
||||
def clear_all(nil), do: clear_all()
|
||||
def clear_all(pid), do: GenServer.call(pid, :clear_all)
|
||||
|
||||
@doc """
|
||||
Clear the given notification.
|
||||
"""
|
||||
def clear(notification), do: clear(__MODULE__, notification)
|
||||
def clear(nil, notification), do: clear(notification)
|
||||
def clear(pid, notification), do: GenServer.call(pid, {:clear, notification})
|
||||
|
||||
@doc """
|
||||
Clear a notification at the given index.
|
||||
"""
|
||||
def clear_at(index), do: clear_at(__MODULE__, index)
|
||||
def clear_at(nil, index), do: clear_at(index)
|
||||
def clear_at(pid, index), do: GenServer.call(pid, {:clear_at, index})
|
||||
|
||||
defp broadcast(notifications) do
|
||||
PubSub.broadcast(
|
||||
Domain.PubSub,
|
||||
@topic,
|
||||
{:notifications, notifications}
|
||||
)
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def init(notifications) do
|
||||
{:ok, notifications}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call(:current, _from, notifications) do
|
||||
{:reply, notifications, notifications}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call({:add, notification}, _from, notifications) do
|
||||
new_notifications = [notification | notifications]
|
||||
broadcast(new_notifications)
|
||||
|
||||
{:reply, :ok, new_notifications}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call(:clear_all, _from, _notifications) do
|
||||
broadcast([])
|
||||
|
||||
{:reply, :ok, []}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call({:clear, notification}, _from, notifications) do
|
||||
new_notifications = Enum.reject(notifications, &(&1 == notification))
|
||||
broadcast(new_notifications)
|
||||
|
||||
{:reply, :ok, new_notifications}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def handle_call({:clear_at, index}, _from, notifications) do
|
||||
{_, new_notifications} = List.pop_at(notifications, index)
|
||||
broadcast(new_notifications)
|
||||
|
||||
{:reply, :ok, new_notifications}
|
||||
end
|
||||
end
|
||||
@@ -1,17 +1,18 @@
|
||||
defmodule FzHttp.Release do
|
||||
alias FzHttp.{ApiTokens, Users}
|
||||
defmodule Domain.Release do
|
||||
alias Domain.{ApiTokens, Users}
|
||||
require Logger
|
||||
|
||||
def migrate do
|
||||
load_app()
|
||||
@app :domain
|
||||
@repos Application.compile_env!(:domain, :ecto_repos)
|
||||
|
||||
for repo <- FzHttp.Config.fetch_env!(:fz_http, :ecto_repos) do
|
||||
def migrate do
|
||||
for repo <- @repos do
|
||||
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
|
||||
end
|
||||
end
|
||||
|
||||
def create_admin_user do
|
||||
boot_database_app()
|
||||
start_domain_app()
|
||||
|
||||
email = email()
|
||||
|
||||
@@ -48,7 +49,7 @@ defmodule FzHttp.Release do
|
||||
end
|
||||
|
||||
def create_api_token(device \\ :stdio) do
|
||||
boot_database_app()
|
||||
start_domain_app()
|
||||
|
||||
device
|
||||
|> IO.write(default_admin_user() |> mint_jwt())
|
||||
@@ -69,16 +70,8 @@ defmodule FzHttp.Release do
|
||||
Users.update_user(user, %{role: role})
|
||||
end
|
||||
|
||||
def repos do
|
||||
FzHttp.Config.fetch_env!(:fz_http, :ecto_repos)
|
||||
end
|
||||
|
||||
defp email do
|
||||
FzHttp.Config.fetch_env!(:fz_http, :admin_email)
|
||||
end
|
||||
|
||||
defp set_supervision_tree_mode(mode) do
|
||||
Application.put_env(:fz_http, :supervision_tree_mode, mode)
|
||||
Domain.Config.fetch_env!(:domain, :admin_email)
|
||||
end
|
||||
|
||||
defp default_admin_user do
|
||||
@@ -90,29 +83,19 @@ defmodule FzHttp.Release do
|
||||
|
||||
defp mint_jwt(%Users.User{} = user) do
|
||||
{:ok, api_token} = ApiTokens.create_api_token(user, %{})
|
||||
{:ok, secret, _claims} = FzHttpWeb.Auth.JSON.Authentication.fz_encode_and_sign(api_token)
|
||||
{:ok, secret, _claims} = Web.Auth.JSON.Authentication.fz_encode_and_sign(api_token)
|
||||
secret
|
||||
end
|
||||
|
||||
defp boot_database_app do
|
||||
load_app()
|
||||
set_supervision_tree_mode(:database)
|
||||
start_app()
|
||||
end
|
||||
defp start_domain_app do
|
||||
# Load the app
|
||||
:ok = Application.ensure_loaded(@app)
|
||||
|
||||
defp load_app do
|
||||
Application.load(:fz_http)
|
||||
|
||||
# Fixes ssl startup when connecting to SSL DBs.
|
||||
# See https://elixirforum.com/t/ssl-connection-cannot-be-established-using-elixir-releases/25444/5
|
||||
Application.ensure_all_started(:ssl)
|
||||
end
|
||||
|
||||
defp start_app do
|
||||
Application.ensure_all_started(:fz_http)
|
||||
# Start the app dependencies
|
||||
{:ok, _apps} = Application.ensure_all_started(@app)
|
||||
end
|
||||
|
||||
defp default_password do
|
||||
FzHttp.Config.fetch_env!(:fz_http, :default_admin_password)
|
||||
Domain.Config.fetch_env!(:domain, :default_admin_password)
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.Repo do
|
||||
defmodule Domain.Repo do
|
||||
use Ecto.Repo,
|
||||
otp_app: :fz_http,
|
||||
otp_app: :domain,
|
||||
adapter: Ecto.Adapters.Postgres
|
||||
|
||||
@doc """
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.Rules do
|
||||
alias FzHttp.{Repo, Auth, Validator, Telemetry}
|
||||
alias FzHttp.Rules.{Authorizer, Rule}
|
||||
defmodule Domain.Rules do
|
||||
alias Domain.{Repo, Auth, Validator, Telemetry}
|
||||
alias Domain.Rules.{Authorizer, Rule}
|
||||
|
||||
def fetch_count_by_user_id(user_id, %Auth.Subject{} = subject) do
|
||||
if Validator.valid_uuid?(user_id) do
|
||||
@@ -99,19 +99,13 @@ defmodule FzHttp.Rules do
|
||||
}
|
||||
end
|
||||
|
||||
def port_rules_supported?, do: FzHttp.Config.fetch_env!(:fz_wall, :port_based_rules_supported)
|
||||
|
||||
def as_settings do
|
||||
port_rules_supported?()
|
||||
|> scope()
|
||||
Rule.Query.by_empty_port_type()
|
||||
|> Repo.all()
|
||||
|> Enum.map(&setting_projection/1)
|
||||
|> MapSet.new()
|
||||
end
|
||||
|
||||
defp scope(true), do: Rule.Query.all()
|
||||
defp scope(false), do: Rule.Query.by_empty_port_type()
|
||||
|
||||
def allowlist do
|
||||
Rule.Query.by_action(:accept)
|
||||
|> Repo.all()
|
||||
@@ -1,10 +1,10 @@
|
||||
defmodule FzHttp.Rules.Authorizer do
|
||||
use FzHttp.Auth.Authorizer
|
||||
alias FzHttp.Rules.Rule
|
||||
defmodule Domain.Rules.Authorizer do
|
||||
use Domain.Auth.Authorizer
|
||||
alias Domain.Rules.Rule
|
||||
|
||||
def manage_rules_permission, do: build(Rule, :manage)
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def list_permissions_for_role(:admin) do
|
||||
[
|
||||
manage_rules_permission()
|
||||
@@ -15,7 +15,7 @@ defmodule FzHttp.Rules.Authorizer do
|
||||
[]
|
||||
end
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def for_subject(queryable, %Subject{} = subject) when is_user(subject) do
|
||||
cond do
|
||||
has_permission?(subject, manage_rules_permission()) ->
|
||||
14
apps/domain/lib/domain/rules/rule.ex
Normal file
14
apps/domain/lib/domain/rules/rule.ex
Normal file
@@ -0,0 +1,14 @@
|
||||
defmodule Domain.Rules.Rule do
|
||||
use Domain, :schema
|
||||
|
||||
schema "rules" do
|
||||
field :action, Ecto.Enum, values: [:drop, :accept], default: :drop
|
||||
field :destination, Domain.Types.INET
|
||||
field :port_type, Ecto.Enum, values: [:tcp, :udp]
|
||||
field :port_range, Domain.Types.Int4Range
|
||||
|
||||
belongs_to :user, Domain.Users.User
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
||||
@@ -1,13 +1,12 @@
|
||||
defmodule FzHttp.Rules.Rule.Changeset do
|
||||
use FzHttp, :changeset
|
||||
alias FzHttp.Rules.Rule
|
||||
defmodule Domain.Rules.Rule.Changeset do
|
||||
use Domain, :changeset
|
||||
alias Domain.Rules.Rule
|
||||
|
||||
@exclusion_msg "destination overlaps with an existing rule"
|
||||
@port_range_msg "port is not within valid range"
|
||||
@port_type_msg "port_type must be specified with port_range"
|
||||
|
||||
@fields ~w[action destination port_type port_range user_id]a
|
||||
@port_based_fields ~w[port_type port_range]a
|
||||
@required_fields ~w[action destination]a
|
||||
|
||||
def create_changeset(attrs) do
|
||||
@@ -15,15 +14,8 @@ defmodule FzHttp.Rules.Rule.Changeset do
|
||||
end
|
||||
|
||||
def update_changeset(rule, attrs) do
|
||||
fields =
|
||||
if FzHttp.Rules.port_rules_supported?() do
|
||||
@fields
|
||||
else
|
||||
@fields -- @port_based_fields
|
||||
end
|
||||
|
||||
rule
|
||||
|> cast(attrs, fields)
|
||||
|> cast(attrs, @fields)
|
||||
|> validate_required(@required_fields)
|
||||
|> validate_required_group(~w[port_range port_type]a)
|
||||
|> check_constraint(:port_range,
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.Rules.Rule.Query do
|
||||
use FzHttp, :query
|
||||
defmodule Domain.Rules.Rule.Query do
|
||||
use Domain, :query
|
||||
|
||||
def all do
|
||||
from(rules in FzHttp.Rules.Rule, as: :rules)
|
||||
from(rules in Domain.Rules.Rule, as: :rules)
|
||||
end
|
||||
|
||||
def by_id(queryable \\ all(), id) do
|
||||
@@ -1,10 +1,10 @@
|
||||
defmodule FzHttp.Telemetry do
|
||||
defmodule Domain.Telemetry do
|
||||
@moduledoc """
|
||||
Functions for various telemetry events.
|
||||
"""
|
||||
use Supervisor
|
||||
alias FzHttp.{Devices, Auth.MFA, Users}
|
||||
alias FzHttp.Telemetry.{Timer, PostHog}
|
||||
alias Domain.{Devices, Auth.MFA, Users}
|
||||
alias Domain.Telemetry.{Timer, PostHog}
|
||||
require Logger
|
||||
|
||||
def start_link(opts) do
|
||||
@@ -12,7 +12,7 @@ defmodule FzHttp.Telemetry do
|
||||
end
|
||||
|
||||
def init(_opts) do
|
||||
config = FzHttp.Config.fetch_env!(:fz_http, FzHttp.Telemetry)
|
||||
config = Domain.Config.fetch_env!(:domain, Domain.Telemetry)
|
||||
|
||||
if Keyword.fetch!(config, :enabled) == true do
|
||||
children = [Timer]
|
||||
@@ -79,8 +79,8 @@ defmodule FzHttp.Telemetry do
|
||||
:ok
|
||||
end
|
||||
|
||||
def fz_http_started do
|
||||
PostHog.capture("fz_http_started", common_fields())
|
||||
def domain_started do
|
||||
PostHog.capture("domain_started", common_fields())
|
||||
:ok
|
||||
end
|
||||
|
||||
@@ -101,7 +101,7 @@ defmodule FzHttp.Telemetry do
|
||||
disable_vpn_on_oidc_error: {_, disable_vpn_on_oidc_error},
|
||||
logo: {_, logo}
|
||||
} =
|
||||
FzHttp.Config.fetch_source_and_configs!([
|
||||
Domain.Config.fetch_source_and_configs!([
|
||||
:openid_connect_providers,
|
||||
:saml_identity_providers,
|
||||
:allow_unprivileged_device_management,
|
||||
@@ -127,10 +127,10 @@ defmodule FzHttp.Telemetry do
|
||||
unprivileged_device_configuration: allow_unprivileged_device_configuration,
|
||||
local_authentication: local_auth_enabled,
|
||||
disable_vpn_on_oidc_error: disable_vpn_on_oidc_error,
|
||||
outbound_email: FzHttpWeb.Mailer.active?(),
|
||||
outbound_email: Web.Mailer.active?(),
|
||||
external_database:
|
||||
external_database?(Map.new(FzHttp.Config.fetch_env!(:fz_http, FzHttp.Repo))),
|
||||
logo_type: FzHttp.Config.Logo.type(logo)
|
||||
external_database?(Map.new(Domain.Config.fetch_env!(:domain, Domain.Repo))),
|
||||
logo_type: Domain.Config.Logo.type(logo)
|
||||
]
|
||||
end
|
||||
|
||||
@@ -148,19 +148,19 @@ defmodule FzHttp.Telemetry do
|
||||
end
|
||||
|
||||
def id do
|
||||
FzHttp.Config.fetch_env!(:fz_http, __MODULE__)
|
||||
Domain.Config.fetch_env!(:domain, __MODULE__)
|
||||
|> Keyword.fetch!(:id)
|
||||
end
|
||||
|
||||
defp fqdn do
|
||||
:fz_http
|
||||
|> FzHttp.Config.fetch_env!(FzHttpWeb.Endpoint)
|
||||
:web
|
||||
|> Domain.Config.fetch_env!(Web.Endpoint)
|
||||
|> Keyword.get(:url)
|
||||
|> Keyword.get(:host)
|
||||
end
|
||||
|
||||
defp version do
|
||||
Application.spec(:fz_http, :vsn) |> to_string()
|
||||
Application.spec(:domain, :vsn) |> to_string()
|
||||
end
|
||||
|
||||
defp external_database?(repo_conf) when is_map_key(repo_conf, :hostname) do
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Telemetry.PostHog do
|
||||
defmodule Domain.Telemetry.PostHog do
|
||||
require Logger
|
||||
|
||||
def capture(event, metadata) do
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.Telemetry.Timer do
|
||||
defmodule Domain.Telemetry.Timer do
|
||||
use GenServer
|
||||
alias FzHttp.Telemetry
|
||||
alias Domain.Telemetry
|
||||
|
||||
@initial_delay 60 * 1_000
|
||||
@interval 43_200
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Types.CIDR do
|
||||
defmodule Domain.Types.CIDR do
|
||||
@moduledoc """
|
||||
Ecto type implementation for CIDR's based on `Postgrex.INET` type, it required netmask to be always set.
|
||||
"""
|
||||
@@ -147,5 +147,5 @@ defmodule FzHttp.Types.CIDR do
|
||||
def load(%Postgrex.INET{} = inet), do: {:ok, inet}
|
||||
def load(_), do: :error
|
||||
|
||||
def to_string(%Postgrex.INET{} = inet), do: FzHttp.Types.INET.to_string(inet)
|
||||
def to_string(%Postgrex.INET{} = inet), do: Domain.Types.INET.to_string(inet)
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Types.INET do
|
||||
defmodule Domain.Types.INET do
|
||||
@moduledoc """
|
||||
INET is an implementation for native PostgreSQL `inet` type which can hold
|
||||
either a CIDR (IP with a netmask) or just an IP address (with empty netmask).
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Types.Int4Range do
|
||||
defmodule Domain.Types.Int4Range do
|
||||
@moduledoc """
|
||||
Ecto type for Postgres' Int4Range type.any()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Types.IP do
|
||||
defmodule Domain.Types.IP do
|
||||
@moduledoc """
|
||||
Ecto type implementation for IP's based on `Postgrex.INET` type,
|
||||
it always ignores netmask by setting it to `nil`.
|
||||
@@ -14,7 +14,7 @@ defmodule FzHttp.Types.IP do
|
||||
def cast(%Postgrex.INET{} = inet), do: {:ok, inet}
|
||||
|
||||
def cast(binary) when is_binary(binary) do
|
||||
with {:ok, address} <- FzHttp.Types.IPPort.cast_address(binary) do
|
||||
with {:ok, address} <- Domain.Types.IPPort.cast_address(binary) do
|
||||
{:ok, %Postgrex.INET{address: address, netmask: nil}}
|
||||
else
|
||||
{:error, _reason} -> {:error, message: "is invalid"}
|
||||
@@ -30,5 +30,5 @@ defmodule FzHttp.Types.IP do
|
||||
def load(_), do: :error
|
||||
|
||||
def to_string(ip) when is_binary(ip), do: ip
|
||||
def to_string(%Postgrex.INET{} = inet), do: FzHttp.Types.INET.to_string(inet)
|
||||
def to_string(%Postgrex.INET{} = inet), do: Domain.Types.INET.to_string(inet)
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Types.IPPort do
|
||||
defmodule Domain.Types.IPPort do
|
||||
@behaviour Ecto.Type
|
||||
|
||||
defstruct [:type, :address, :port]
|
||||
21
apps/domain/lib/domain/types/protocols.ex
Normal file
21
apps/domain/lib/domain/types/protocols.ex
Normal file
@@ -0,0 +1,21 @@
|
||||
defimpl String.Chars, for: Postgrex.INET do
|
||||
def to_string(%Postgrex.INET{} = inet), do: Domain.Types.INET.to_string(inet)
|
||||
end
|
||||
|
||||
defimpl Phoenix.HTML.Safe, for: Postgrex.INET do
|
||||
def to_iodata(%Postgrex.INET{} = inet), do: Domain.Types.INET.to_string(inet)
|
||||
end
|
||||
|
||||
defimpl Jason.Encoder, for: Postgrex.INET do
|
||||
def encode(%Postgrex.INET{} = struct, opts) do
|
||||
Jason.Encode.string("#{struct}", opts)
|
||||
end
|
||||
end
|
||||
|
||||
defimpl String.Chars, for: Domain.Types.IPPort do
|
||||
def to_string(%Domain.Types.IPPort{} = ip_port), do: Domain.Types.IPPort.to_string(ip_port)
|
||||
end
|
||||
|
||||
defimpl Phoenix.HTML.Safe, for: Domain.Types.IPPort do
|
||||
def to_iodata(%Domain.Types.IPPort{} = ip_port), do: Domain.Types.IPPort.to_string(ip_port)
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.Users do
|
||||
alias FzHttp.{Repo, Auth, Validator, Config, Telemetry}
|
||||
alias FzHttp.Users.{Authorizer, User}
|
||||
defmodule Domain.Users do
|
||||
alias Domain.{Repo, Auth, Validator, Config, Telemetry}
|
||||
alias Domain.Users.{Authorizer, User}
|
||||
require Ecto.Query
|
||||
|
||||
def count do
|
||||
@@ -85,7 +85,7 @@ defmodule FzHttp.Users do
|
||||
end
|
||||
|
||||
def consume_sign_in_token(%User{} = user, token) when is_binary(token) do
|
||||
if FzHttp.Crypto.equal?(token, user.sign_in_token_hash) do
|
||||
if Domain.Crypto.equal?(token, user.sign_in_token_hash) do
|
||||
User.Query.by_id(user.id)
|
||||
|> User.Query.where_sign_in_token_is_not_expired()
|
||||
|> Ecto.Query.update(set: [sign_in_token_hash: nil, sign_in_token_created_at: nil])
|
||||
@@ -151,8 +151,8 @@ defmodule FzHttp.Users do
|
||||
|> Repo.update()
|
||||
|> case do
|
||||
{:ok, user} ->
|
||||
FzHttp.Telemetry.disable_user()
|
||||
FzHttpWeb.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{})
|
||||
Domain.Telemetry.disable_user()
|
||||
Web.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{})
|
||||
{:ok, user}
|
||||
|
||||
{:error, reason} ->
|
||||
@@ -1,11 +1,11 @@
|
||||
defmodule FzHttp.Users.Authorizer do
|
||||
use FzHttp.Auth.Authorizer
|
||||
alias FzHttp.Users.User
|
||||
defmodule Domain.Users.Authorizer do
|
||||
use Domain.Auth.Authorizer
|
||||
alias Domain.Users.User
|
||||
|
||||
def manage_users_permission, do: build(User, :manage)
|
||||
def edit_own_profile_permission, do: build(User, :edit_own_profile)
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def list_permissions_for_role(:admin) do
|
||||
[
|
||||
manage_users_permission(),
|
||||
@@ -23,7 +23,7 @@ defmodule FzHttp.Users.Authorizer do
|
||||
[]
|
||||
end
|
||||
|
||||
@impl FzHttp.Auth.Authorizer
|
||||
@impl Domain.Auth.Authorizer
|
||||
def for_subject(queryable, %Subject{} = subject) when is_user(subject) do
|
||||
cond do
|
||||
has_permission?(subject, manage_users_permission()) ->
|
||||
@@ -1,5 +1,5 @@
|
||||
defmodule FzHttp.Users.User do
|
||||
use FzHttp, :schema
|
||||
defmodule Domain.Users.User do
|
||||
use Domain, :schema
|
||||
|
||||
schema "users" do
|
||||
field :role, Ecto.Enum, values: [:unprivileged, :admin]
|
||||
@@ -20,9 +20,9 @@ defmodule FzHttp.Users.User do
|
||||
# Virtual fields that can be hydrated
|
||||
field :device_count, :integer, virtual: true
|
||||
|
||||
has_many :devices, FzHttp.Devices.Device
|
||||
has_many :oidc_connections, FzHttp.Auth.OIDC.Connection
|
||||
has_many :api_tokens, FzHttp.ApiTokens.ApiToken
|
||||
has_many :devices, Domain.Devices.Device
|
||||
has_many :oidc_connections, Domain.Auth.OIDC.Connection
|
||||
has_many :api_tokens, Domain.ApiTokens.ApiToken
|
||||
|
||||
field :disabled_at, :utc_datetime_usec
|
||||
timestamps()
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule FzHttp.Users.User.Changeset do
|
||||
use FzHttp, :changeset
|
||||
alias FzHttp.Auth
|
||||
alias FzHttp.Users
|
||||
defmodule Domain.Users.User.Changeset do
|
||||
use Domain, :changeset
|
||||
alias Domain.Auth
|
||||
alias Domain.Users
|
||||
|
||||
@min_password_length 12
|
||||
@max_password_length 64
|
||||
@@ -75,7 +75,7 @@ defmodule FzHttp.Users.User.Changeset do
|
||||
def generate_sign_in_token(%Users.User{} = user) do
|
||||
user
|
||||
|> change()
|
||||
|> put_change(:sign_in_token, FzHttp.Crypto.rand_string())
|
||||
|> put_change(:sign_in_token, Domain.Crypto.rand_string())
|
||||
|> put_hash(:sign_in_token, to: :sign_in_token_hash)
|
||||
|> put_change(:sign_in_token_created_at, DateTime.utc_now())
|
||||
end
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.Users.User.Query do
|
||||
use FzHttp, :query
|
||||
defmodule Domain.Users.User.Query do
|
||||
use Domain, :query
|
||||
|
||||
def all do
|
||||
from(users in FzHttp.Users.User, as: :users)
|
||||
from(users in Domain.Users.User, as: :users)
|
||||
end
|
||||
|
||||
def by_id(queryable \\ all(), id)
|
||||
@@ -1,4 +1,4 @@
|
||||
defmodule FzHttp.Validator do
|
||||
defmodule Domain.Validator do
|
||||
@doc """
|
||||
A set of changeset helpers and schema extensions to simplify our changesets and make validation more reliable.
|
||||
"""
|
||||
@@ -156,7 +156,7 @@ defmodule FzHttp.Validator do
|
||||
|
||||
def validate_in_cidr(changeset, ip_field, cidr) do
|
||||
validate_change(changeset, ip_field, fn _ip_field, ip ->
|
||||
if FzHttp.Types.CIDR.contains?(cidr, ip) do
|
||||
if Domain.Types.CIDR.contains?(cidr, ip) do
|
||||
[]
|
||||
else
|
||||
[{ip_field, "is not in the CIDR #{cidr}"}]
|
||||
@@ -166,7 +166,7 @@ defmodule FzHttp.Validator do
|
||||
|
||||
def validate_cidr(changeset, field, _opts \\ []) do
|
||||
validate_change(changeset, field, fn _current_field, value ->
|
||||
case FzHttp.Types.CIDR.cast(value) do
|
||||
case Domain.Types.CIDR.cast(value) do
|
||||
{:ok, _cidr} ->
|
||||
[]
|
||||
|
||||
@@ -226,7 +226,7 @@ defmodule FzHttp.Validator do
|
||||
def put_hash(%Ecto.Changeset{} = changeset, value_field, to: hash_field) do
|
||||
with {:ok, value} when is_binary(value) and value != "" <-
|
||||
fetch_change(changeset, value_field) do
|
||||
put_change(changeset, hash_field, FzHttp.Crypto.hash(value))
|
||||
put_change(changeset, hash_field, Domain.Crypto.hash(value))
|
||||
else
|
||||
_ -> changeset
|
||||
end
|
||||
@@ -238,7 +238,7 @@ defmodule FzHttp.Validator do
|
||||
def validate_hash(changeset, value_field, hash_field: hash_field) do
|
||||
with {:data, hash} <- fetch_field(changeset, hash_field) do
|
||||
validate_change(changeset, value_field, fn value_field, token ->
|
||||
if FzHttp.Crypto.equal?(token, hash) do
|
||||
if Domain.Crypto.equal?(token, hash) do
|
||||
[]
|
||||
else
|
||||
[{value_field, {"is invalid", [validation: :hash]}}]
|
||||
6
apps/domain/lib/domain/vault.ex
Normal file
6
apps/domain/lib/domain/vault.ex
Normal file
@@ -0,0 +1,6 @@
|
||||
defmodule Domain.Vault do
|
||||
@moduledoc """
|
||||
Manages encrypted DB fields.
|
||||
"""
|
||||
use Cloak.Vault, otp_app: :domain
|
||||
end
|
||||
86
apps/domain/mix.exs
Normal file
86
apps/domain/mix.exs
Normal file
@@ -0,0 +1,86 @@
|
||||
defmodule Domain.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :domain,
|
||||
version: version(),
|
||||
build_path: "../../_build",
|
||||
config_path: "../../config/config.exs",
|
||||
deps_path: "../../deps",
|
||||
lockfile: "../../mix.lock",
|
||||
elixir: "~> 1.12",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: Mix.compilers(),
|
||||
start_permanent: Mix.env() == :prod,
|
||||
test_coverage: [tool: ExCoveralls],
|
||||
preferred_cli_env: [
|
||||
coveralls: :test,
|
||||
"coveralls.detail": :test,
|
||||
"coveralls.post": :test,
|
||||
"coveralls.html": :test
|
||||
],
|
||||
aliases: aliases(),
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
def version do
|
||||
# Use dummy version for dev and test
|
||||
System.get_env("VERSION", "0.0.0+git.0.deadbeef")
|
||||
end
|
||||
|
||||
def application do
|
||||
[
|
||||
mod: {Domain.Application, []},
|
||||
extra_applications: [
|
||||
:logger,
|
||||
:runtime_tools
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
# Specifies which paths to compile per environment.
|
||||
defp elixirc_paths(:test), do: ["test/support", "lib"]
|
||||
defp elixirc_paths(_), do: ["lib"]
|
||||
|
||||
defp deps do
|
||||
[
|
||||
# Ecto-related deps
|
||||
{:postgrex, "~> 0.16"},
|
||||
{:decimal, "~> 2.0"},
|
||||
{:ecto_sql, "~> 3.7"},
|
||||
{:cloak, "~> 1.1"},
|
||||
{:cloak_ecto, "~> 1.2"},
|
||||
|
||||
# PubSub
|
||||
{:phoenix_pubsub, "~> 2.0"},
|
||||
|
||||
# Auth-related deps
|
||||
{:plug_crypto, "~> 1.2"},
|
||||
{:openid_connect, github: "firezone/openid_connect", branch: "master"},
|
||||
{:argon2_elixir, "~> 2.0"},
|
||||
{:nimble_totp, "~> 0.2"},
|
||||
|
||||
# Other deps
|
||||
{:telemetry, "~> 1.0"},
|
||||
{:posthog, "~> 0.1"},
|
||||
|
||||
# Runtime debugging
|
||||
{:recon, "~> 2.5"},
|
||||
{:observer_cli, "~> 1.7"},
|
||||
|
||||
# Test and dev deps
|
||||
{:bypass, "~> 2.1", only: :test}
|
||||
]
|
||||
end
|
||||
|
||||
defp aliases do
|
||||
[
|
||||
"ecto.seed": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
|
||||
"ecto.setup": ["ecto.create", "ecto.migrate"],
|
||||
"ecto.reset": ["ecto.drop", "ecto.setup"],
|
||||
test: ["ecto.create --quiet", "ecto.migrate", "test"]
|
||||
]
|
||||
end
|
||||
end
|
||||
@@ -1,10 +1,10 @@
|
||||
defmodule FzHttp.ApiTokensTest do
|
||||
use FzHttp.DataCase, async: true
|
||||
import FzHttp.ApiTokens
|
||||
alias FzHttp.ApiTokens.{ApiToken, Authorizer}
|
||||
alias FzHttp.ApiTokensFixtures
|
||||
alias FzHttp.SubjectFixtures
|
||||
alias FzHttp.UsersFixtures
|
||||
defmodule Domain.ApiTokensTest do
|
||||
use Domain.DataCase, async: true
|
||||
import Domain.ApiTokens
|
||||
alias Domain.ApiTokens.{ApiToken, Authorizer}
|
||||
alias Domain.ApiTokensFixtures
|
||||
alias Domain.SubjectFixtures
|
||||
alias Domain.UsersFixtures
|
||||
|
||||
setup do
|
||||
user = UsersFixtures.create_user_with_role(:admin)
|
||||
@@ -1,8 +1,8 @@
|
||||
defmodule FzHttp.Auth.MFATest do
|
||||
use FzHttp.DataCase, async: true
|
||||
alias FzHttp.UsersFixtures
|
||||
alias FzHttp.MFAFixtures
|
||||
alias FzHttp.Auth.MFA
|
||||
defmodule Domain.Auth.MFATest do
|
||||
use Domain.DataCase, async: true
|
||||
alias Domain.UsersFixtures
|
||||
alias Domain.MFAFixtures
|
||||
alias Domain.Auth.MFA
|
||||
|
||||
describe "count_users_with_mfa_enabled/0" do
|
||||
test "returns 0 when there are no methods" do
|
||||
@@ -1,14 +1,14 @@
|
||||
defmodule FzHttp.Auth.OIDC.RefresherTest do
|
||||
use FzHttp.DataCase, async: true
|
||||
alias FzHttp.Auth.OIDC.Refresher
|
||||
alias FzHttp.UsersFixtures
|
||||
defmodule Domain.Auth.OIDC.RefresherTest do
|
||||
use Domain.DataCase, async: true
|
||||
alias Domain.Auth.OIDC.Refresher
|
||||
alias Domain.UsersFixtures
|
||||
|
||||
setup do
|
||||
user = UsersFixtures.create_user_with_role(:admin)
|
||||
{bypass, [provider_attrs]} = FzHttp.ConfigFixtures.start_openid_providers(["google"])
|
||||
{bypass, [provider_attrs]} = Domain.ConfigFixtures.start_openid_providers(["google"])
|
||||
|
||||
conn =
|
||||
Repo.insert!(%FzHttp.Auth.OIDC.Connection{
|
||||
Repo.insert!(%Domain.Auth.OIDC.Connection{
|
||||
user_id: user.id,
|
||||
provider: "google",
|
||||
refresh_token: "REFRESH_TOKEN"
|
||||
@@ -19,7 +19,7 @@ defmodule FzHttp.Auth.OIDC.RefresherTest do
|
||||
|
||||
describe "refresh failed" do
|
||||
test "disable user", %{user: user, conn: conn, bypass: bypass} do
|
||||
FzHttp.ConfigFixtures.expect_refresh_token_failure(bypass)
|
||||
Domain.ConfigFixtures.expect_refresh_token_failure(bypass)
|
||||
|
||||
assert Refresher.refresh(user.id) == {:stop, :shutdown, user.id}
|
||||
user = Repo.reload(user)
|
||||
@@ -32,7 +32,7 @@ defmodule FzHttp.Auth.OIDC.RefresherTest do
|
||||
|
||||
describe "refresh succeeded" do
|
||||
test "does not change user", %{user: user, conn: conn, bypass: bypass} do
|
||||
FzHttp.ConfigFixtures.expect_refresh_token(bypass)
|
||||
Domain.ConfigFixtures.expect_refresh_token(bypass)
|
||||
|
||||
assert Refresher.refresh(user.id) == {:stop, :shutdown, user.id}
|
||||
user = Repo.reload(user)
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule FzHttp.AuthTest do
|
||||
use FzHttp.DataCase
|
||||
import FzHttp.Auth
|
||||
alias FzHttp.ConfigFixtures
|
||||
defmodule Domain.AuthTest do
|
||||
use Domain.DataCase
|
||||
import Domain.Auth
|
||||
alias Domain.ConfigFixtures
|
||||
|
||||
describe "fetch_oidc_provider_config/1" do
|
||||
test "returns error when provider does not exist" do
|
||||
@@ -25,7 +25,7 @@ defmodule FzHttp.AuthTest do
|
||||
end
|
||||
|
||||
test "puts default redirect_uri" do
|
||||
FzHttp.Config.put_env_override(:external_url, "http://foo.bar.com/")
|
||||
Domain.Config.put_env_override(:web, :external_url, "http://foo.bar.com/")
|
||||
|
||||
{_bypass, [attrs]} =
|
||||
ConfigFixtures.start_openid_providers(["google"], %{"redirect_uri" => nil})
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule FzHttp.Config.CasterTest do
|
||||
defmodule Domain.Config.CasterTest do
|
||||
use ExUnit.Case, async: true
|
||||
import FzHttp.Config.Caster
|
||||
import Domain.Config.Caster
|
||||
|
||||
describe "cast/2" do
|
||||
test "casts a binary to an array of integers" do
|
||||
@@ -1,15 +1,15 @@
|
||||
defmodule FzHttp.Config.DefinitionTest do
|
||||
defmodule Domain.Config.DefinitionTest do
|
||||
use ExUnit.Case, async: true
|
||||
import FzHttp.Config.Definition
|
||||
import Domain.Config.Definition
|
||||
|
||||
defmodule InvalidDefinitions do
|
||||
use FzHttp.Config.Definition
|
||||
use Domain.Config.Definition
|
||||
|
||||
defconfig(:required, Types.IP, foo: :bar)
|
||||
end
|
||||
|
||||
defmodule Definitions do
|
||||
use FzHttp.Config.Definition
|
||||
use Domain.Config.Definition
|
||||
|
||||
defconfig(:required, Types.IP)
|
||||
|
||||
@@ -57,7 +57,7 @@ defmodule FzHttp.Config.DefinitionTest do
|
||||
end
|
||||
|
||||
test "inserts a function which returns definition doc" do
|
||||
assert fetch_doc(FzHttp.Config.Definitions, :default_admin_email) ==
|
||||
assert fetch_doc(Domain.Config.Definitions, :default_admin_email) ==
|
||||
{:ok, "Primary administrator email.\n"}
|
||||
|
||||
assert fetch_doc(Foo, :bar) ==
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user