Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -332,35 +332,35 @@ jobs:

build-containers:
name: Build containers
needs:
- static-code-analysis
- test
if: (github.event_name == 'push' && github.ref_name == 'main') || github.event_name == 'workflow_dispatch'
# needs:
# - static-code-analysis
# - test
# if: (github.event_name == 'push' && github.ref_name == 'main') || github.event_name == 'workflow_dispatch'
uses: trento-project/web/.github/workflows/build-containers.yaml@main
strategy:
matrix:
include:
- MIX_ENV: prod
tag: "${{ (github.event_name == 'push' && github.ref_name == 'main' && 'rolling') || github.sha }}"
tag: tesing-introspection
- MIX_ENV: demo
tag: demo
tag: tesing-introspection
with:
image_name: trento-wanda
MIX_ENV: ${{ matrix.MIX_ENV }}
tag: ${{ matrix.tag }}

deploy-demo:
runs-on: ubuntu-24.04
if: vars.DEPLOY_DEMO == 'true'
needs:
- build-containers
steps:
- name: Remotely trigger trento-web demo deployment
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.WEB_REPO_DISPATCH_PAT }}
repository: ${{ github.repository_owner }}/${{ vars.DEMO_TRIGGER_TARGET || 'web' }}
event-type: deploy-demo
# deploy-demo:
# runs-on: ubuntu-24.04
# if: vars.DEPLOY_DEMO == 'true'
# needs:
# - build-containers
# steps:
# - name: Remotely trigger trento-web demo deployment
# uses: peter-evans/repository-dispatch@v3
# with:
# token: ${{ secrets.WEB_REPO_DISPATCH_PAT }}
# repository: ${{ github.repository_owner }}/${{ vars.DEMO_TRIGGER_TARGET || 'web' }}
# event-type: deploy-demo

generate-docs:
name: Generate project documentation
Expand Down
4 changes: 3 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,16 @@ config :rustler_precompiled, :force_build, rhai_rustler: true

config :wanda,
cors_enabled: true,
jwt_authentication_enabled: true,
token_authentication_enabled: true,
operations_enabled: true,
date_service: Wanda.Support.DateService,
oas_server_url: nil

config :bodyguard,
default_error: :forbidden

config :wanda, :auth_server, url: "http://localhost:4000"

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{config_env()}.exs"
15 changes: 12 additions & 3 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ if config_env() in [:prod, :demo] do
origin: [cors_origin]
end

jwt_authentication_enabled = System.get_env("JWT_AUTHENTICATION_ENABLED", "true") == "true"
token_authentication_enabled = System.get_env("TOKEN_AUTHENTICATION_ENABLED", "true") == "true"

config :wanda,
jwt_authentication_enabled: jwt_authentication_enabled
token_authentication_enabled: token_authentication_enabled

if jwt_authentication_enabled do
if token_authentication_enabled do
config :joken,
access_token_signer:
System.get_env("ACCESS_TOKEN_ENC_SECRET") ||
Expand All @@ -133,6 +133,15 @@ if config_env() in [:prod, :demo] do
)
)
]

auth_server_url =
System.get_env("AUTH_SERVER_URL") ||
raise """
environment variable AUTH_SERVER_URL is missing.
For example: http://localhost:4000
"""

config :wanda, :auth_server, url: auth_server_url
end

if config_env() === :demo do
Expand Down
4 changes: 3 additions & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ config :joken,
access_token_signer: "s2ZdE+3+ke1USHEJ5O45KT364KiXPYaB9cJPdH3p60t8yT0nkLexLBNw8TFSzC7k"

config :wanda,
jwt_authentication_enabled: false
token_authentication_enabled: false

config :wanda, Wanda.Executions.FakeGatheredFacts,
demo_facts_config: "test/fixtures/demo/fake_facts_test.yaml"

config :exvcr, global_mock: true
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule WandaWeb.Auth.JWTAuthPlug do
defmodule WandaWeb.Auth.AuthPlug do
@moduledoc """
Plug responsible for reading the JWT from the authorization header and
Plug responsible for reading the Token from the authorization header and
validating it.

If the token is valid, the user_id is added to the private section of the
Expand All @@ -11,26 +11,26 @@ defmodule WandaWeb.Auth.JWTAuthPlug do

import Plug.Conn

alias WandaWeb.Auth.AccessToken
alias WandaWeb.Auth.Client.AuthClient

require Logger

def init(opts), do: opts

@doc """
Read, validate and decode the JWT from authorization header at each call
Read, validate and decode the Token from authorization header at each call
"""
def call(conn, _) do
authenticate(conn)
end

defp authenticate(conn) do
with {:ok, jwt_token} <- read_token(conn),
{:ok, %{"sub" => sub, "abilities" => abilities}} <-
AccessToken.verify_and_validate(jwt_token) do
with {:ok, token} <- read_token(conn),
{:ok, %{active: true, sub: sub, abilities: abilities}} <-
AuthClient.introspect_token(token) do
conn
|> put_private(:user_id, sub)
|> put_private(:abilities, abilities_to_atom_map(abilities))
|> put_private(:abilities, abilities)
else
_ ->
conn
Expand All @@ -52,15 +52,4 @@ defmodule WandaWeb.Auth.JWTAuthPlug do
{:error, :no_token}
end
end

defp abilities_to_atom_map(abilities) when is_list(abilities) do
Enum.map(abilities, fn %{"name" => name, "resource" => resource} ->
%{
name: name,
resource: resource
}
end)
end

defp abilities_to_atom_map(_), do: []
end
19 changes: 19 additions & 0 deletions lib/wanda_web/auth/client/auth_client.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defmodule WandaWeb.Auth.Client.AuthClient do
@moduledoc """
Client for interacting with the authentication server.
"""

@type introspected_token :: %{
active: boolean(),
sub: integer(),
abilities: list(%{name: String.t(), resource: String.t()})
}

@callback introspect_token(token :: String.t()) ::
{:ok, introspected_token()} | {:error, atom()}

def introspect_token(token), do: impl().introspect_token(token)

defp impl,
do: Application.get_env(:wanda, :auth_client, WandaWeb.Auth.Client.HttpClient)
end
59 changes: 59 additions & 0 deletions lib/wanda_web/auth/client/auth_client_http.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
defmodule WandaWeb.Auth.Client.HttpClient do
@moduledoc """
Http AuthClient implementation.
"""

@behaviour WandaWeb.Auth.Client.AuthClient

alias WandaWeb.Auth.Client.AuthClient

require Logger

@impl AuthClient
def introspect_token(token) do
"#{auth_server_url()}/api/session/token/introspect"
|> HTTPoison.post(
Jason.encode!(%{"token" => token}),
[{"Content-type", "application/json"}]
)
|> get_response_body(
error_atom: :unable_to_get_introspect_response,
error_log: "Unable to retrieve token introspection response body"
)
|> decode_response(
error_atom: :cannot_decode_introspect_response,
error_log: "Unable to decode token introspection response body"
)
end

defp get_response_body({:ok, %HTTPoison.Response{status_code: 200, body: body}}, _),
do: {:ok, body}

defp get_response_body(error, error_atom: error_atom, error_log: error_log) do
Logger.error("#{error_log} Error: #{inspect(error)}")

{:error, error_atom}
end

defp decode_response({:ok, body}, error_atom: error_atom, error_log: error_log) do
case Jason.decode(body, keys: :atoms) do
{:ok, _} = result ->
result

error ->
Logger.error("#{error_log} Error: #{inspect(error)}")

{:error, error_atom}
end
end

defp decode_response({:error, _} = error, _) do
Logger.error("Unable to decode response body Error: #{inspect(error)}")

error
end

defp auth_server_url do
Application.fetch_env!(:wanda, :auth_server)[:url]
end
end
4 changes: 2 additions & 2 deletions lib/wanda_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ defmodule WandaWeb.Router do

pipeline :protected_api do
plug Unplug,
if: {Unplug.Predicates.AppConfigEquals, {:wanda, :jwt_authentication_enabled, true}},
do: WandaWeb.Auth.JWTAuthPlug
if: {Unplug.Predicates.AppConfigEquals, {:wanda, :token_authentication_enabled, true}},
do: WandaWeb.Auth.AuthPlug
end

scope "/" do
Expand Down
10 changes: 8 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ defmodule Wanda.MixProject do
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
coveralls: :test,
"coveralls.github": :test
"coveralls.github": :test,
vcr: :test,
"vcr.delete": :test,
"vcr.check": :test,
"vcr.show": :test
],
dialyzer: [plt_add_apps: [:ex_unit]]
]
Expand Down Expand Up @@ -128,7 +132,9 @@ defmodule Wanda.MixProject do
# required overrides to upgrade to elixir 1.15.7 and erlang otp 26
# https://stackoverflow.com/questions/76562092/hi-i-had-created-elixir-project-with-phoenix-framework-there-is-yaml-file-when
{:ecto, "~> 3.10", override: true},
{:bodyguard, "~> 2.4"}
{:bodyguard, "~> 2.4"},
{:httpoison, "~> 2.0"},
{:exvcr, "~> 0.11", only: :test}
]
end

Expand Down
Loading
Loading