-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
1,668 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Used by "mix format" | ||
[ | ||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# 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 third-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"). | ||
bridge_restapi-*.tar | ||
|
||
# Temporary files, for example, from tests. | ||
/tmp/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# BridgeApi | ||
|
||
Channel Sender Rest Api Operations. | ||
|
||
## Installation | ||
|
||
If [available in Hex](https://hex.pm/docs/publish), the package can be installed | ||
by adding `bridge_restapi` to your list of dependencies in `mix.exs`: | ||
|
||
```elixir | ||
def deps do | ||
[ | ||
{:bridge_restapi, "~> 0.1.0"} | ||
] | ||
end | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
defmodule BridgeApi do | ||
@moduledoc """ | ||
Documentation for `ChannelBridgeApi`. | ||
""" | ||
|
||
use Application | ||
|
||
alias BridgeApi.Rest.BaseRouter | ||
|
||
@doc false | ||
@impl Application | ||
def start(_type, _args) do | ||
children = [ | ||
{Plug.Cowboy, | ||
scheme: :http, | ||
plug: BaseRouter, | ||
options: [ | ||
port: BridgeHelperConfig.get([:bridge, "port"], 8080), | ||
protocol_options: [max_keepalive: 2_000, active_n: 200] | ||
]} | ||
] | ||
|
||
opts = [strategy: :one_for_one, name: BridgeApi.Supervisor] | ||
Supervisor.start_link(children, opts) | ||
end | ||
|
||
end |
48 changes: 48 additions & 0 deletions
48
channel-bridge/apps/bridge_restapi/lib/bridge_api/rest/auth_plug.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
defmodule BridgeApi.Rest.AuthPlug do | ||
@moduledoc false | ||
|
||
alias BridgeApi.Rest.Header | ||
|
||
require Logger | ||
|
||
@type token() :: String.t() | ||
@type conn() :: Plug.Conn.t() | ||
|
||
defmodule NoCredentialsError do | ||
@moduledoc """ | ||
Error raised when no credentials are sent in request | ||
""" | ||
|
||
defexception message: "" | ||
end | ||
|
||
import Plug.Conn | ||
|
||
def init(options), do: options | ||
|
||
@doc """ | ||
Performs authentication. The concrete auth is implemented via @behaviour 'AuthProvider', and | ||
defined in configuration env property :channel_authenticator. | ||
""" | ||
def call(conn, _opts) do | ||
|
||
auth_provider = case get_in(Application.get_env(:channel_bridge, :config), [:bridge, "channel_authenticator"]) do | ||
nil -> BridgeRestapiAuth.PassthroughProvider | ||
v -> String.to_existing_atom(v) | ||
end | ||
|
||
with {:ok, all_headers} <- Header.all_headers(conn), | ||
{:ok, claims} <- auth_provider.validate_credentials(all_headers) do | ||
# auth was successful and claims are stored | ||
store_claims_private(claims, conn) | ||
else | ||
{:error, :nocreds} -> | ||
Logger.error("Credentials required for authentication") | ||
raise NoCredentialsError, message: "Credentials required for authentication" | ||
end | ||
end | ||
|
||
defp store_claims_private(claims, conn) do | ||
put_private(conn, :token_claims, claims) | ||
end | ||
end |
15 changes: 15 additions & 0 deletions
15
channel-bridge/apps/bridge_restapi/lib/bridge_api/rest/base_router.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
defmodule BridgeApi.Rest.BaseRouter do | ||
@moduledoc """ | ||
""" | ||
|
||
use Plug.Router | ||
use Plug.ErrorHandler | ||
|
||
plug(:match) | ||
plug(:dispatch) | ||
|
||
forward("/api/v1/channel-bridge-ex", to: BridgeApi.Rest.RestRouter) | ||
|
||
match(_, do: send_resp(conn, 404, "Resource not found")) | ||
|
||
end |
125 changes: 125 additions & 0 deletions
125
channel-bridge/apps/bridge_restapi/lib/bridge_api/rest/channel_request.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
defmodule BridgeApi.Rest.ChannelRequest do | ||
defstruct ~w[req_headers req_params body token_claims]a | ||
|
||
@moduledoc """ | ||
A new channel request data | ||
""" | ||
alias BridgeCore.Utils.JsonSearch | ||
alias BridgeCore.AppClient | ||
alias BridgeCore.User | ||
alias BridgeCore.CloudEvent.Extractor | ||
|
||
require Logger | ||
|
||
@default_sessionid_search_path "$.req_headers['sub']" | ||
@default_app_ref "default_app" | ||
@default_user_ref "default_user" | ||
|
||
@type headers_map() :: map() | ||
@type params_map() :: map() | ||
@type body_map() :: map() | ||
@type claims_map() :: map() | ||
|
||
@type t() :: %__MODULE__{ | ||
req_headers: headers_map(), | ||
req_params: params_map(), | ||
body: body_map(), | ||
token_claims: claims_map() | ||
} | ||
|
||
@doc """ | ||
Creates a Channel Request by consolidating request data. | ||
""" | ||
@spec new(headers_map(), params_map(), body_map(), claims_map()) :: t() | ||
def new(req_headers, req_params, body, token_claims) do | ||
%__MODULE__{ | ||
req_headers: req_headers, | ||
req_params: req_params, | ||
body: body, | ||
token_claims: token_claims | ||
} | ||
end | ||
|
||
@spec extract_channel_alias(t()) :: {:ok, binary()} | {:error, any()} | ||
def extract_channel_alias(request_data) do | ||
|
||
request_channel_alias = | ||
BridgeHelperConfig.get([:bridge, "request_channel_identifier"], @default_sessionid_search_path) | ||
|
||
ch_alias = | ||
request_data | ||
|> JsonSearch.prepare() | ||
|> JsonSearch.extract(request_channel_alias) | ||
|
||
case ch_alias do | ||
e when e in [nil, "undefined"] -> {:error, :nosessionidfound} | ||
_ -> {:ok, ch_alias} | ||
end | ||
end | ||
|
||
@spec extract_application(t()) :: {:ok, AppClient.t()} | {:error, any()} | ||
def extract_application(request_data) do | ||
app_key = BridgeHelperConfig.get([:bridge, "request_app_identifier"], @default_app_ref) | ||
|> (fn x -> | ||
case String.starts_with?(x, "$.") do | ||
true -> {:lookup, x} | ||
false -> {:fixed, x} | ||
end | ||
end).() | ||
|
||
app = | ||
case apply_strategy(app_key, request_data) do | ||
{:ok, app_id} -> | ||
AppClient.new(app_id, "") | ||
|
||
{:error, err} -> | ||
Logger.warning( | ||
"missing application info in request, #{inspect(app_key)} = #{err}. Data: #{inspect(request_data)}" | ||
) | ||
|
||
AppClient.new(@default_app_ref, "") | ||
end | ||
|
||
{:ok, app} | ||
end | ||
|
||
@spec extract_user_info(t()) :: {:ok, User.t()} | {:error, any()} | ||
def extract_user_info(request_data) do | ||
user_key = BridgeHelperConfig.get([:bridge, "request_user_identifier"], @default_user_ref) | ||
|> (fn x -> | ||
case String.starts_with?(x, "$.") do | ||
true -> {:lookup, x} | ||
false -> {:fixed, x} | ||
end | ||
end).() | ||
|
||
user = | ||
case apply_strategy(user_key, request_data) do | ||
{:ok, user_id} -> | ||
User.new(user_id) | ||
|
||
{:error, err} -> | ||
Logger.warning( | ||
"missing user info in request, #{inspect(user_key)} = #{err}. Data: #{inspect(request_data)}" | ||
) | ||
|
||
User.new(@default_app_ref) | ||
end | ||
|
||
{:ok, user} | ||
|
||
end | ||
|
||
defp apply_strategy(config, data) do | ||
case config do | ||
{:fixed, fixed_value} -> | ||
# uses a fixed string value as application reference | ||
{:ok, fixed_value} | ||
|
||
{:lookup, key_to_search} -> | ||
# searches for a key in user data map | ||
Extractor.extract(data, key_to_search) | ||
end | ||
end | ||
|
||
end |
40 changes: 40 additions & 0 deletions
40
channel-bridge/apps/bridge_restapi/lib/bridge_api/rest/error_response.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
defmodule BridgeApi.Rest.ErrorResponse do | ||
@moduledoc """ | ||
Error definition | ||
""" | ||
|
||
@type reason() :: String.t() | ||
@type domain() :: String.t() | ||
@type code() :: String.t() | ||
@type message() :: String.t() | ||
@type type() :: String.t() | ||
|
||
@type t() :: %__MODULE__{ | ||
reason: reason(), | ||
domain: domain(), | ||
code: code(), | ||
message: message(), | ||
type: type() | ||
} | ||
|
||
@derive Jason.Encoder | ||
defstruct reason: nil, | ||
domain: nil, | ||
code: nil, | ||
message: nil, | ||
type: nil | ||
|
||
@doc """ | ||
creates a simple error representation | ||
""" | ||
@spec new(reason(), domain(), code(), message(), type()) :: t() | ||
def new(reason, domain, code, message, type) do | ||
%__MODULE__{ | ||
reason: reason, | ||
domain: domain, | ||
code: code, | ||
message: message, | ||
type: type | ||
} | ||
end | ||
end |
35 changes: 35 additions & 0 deletions
35
channel-bridge/apps/bridge_restapi/lib/bridge_api/rest/header.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
defmodule BridgeApi.Rest.Header do | ||
@moduledoc """ | ||
Helper Function for extractig header values | ||
""" | ||
import Plug.Conn | ||
|
||
@type header_name :: String.t() | ||
@type header_value :: String.t() | ||
@type headers_map :: map() | ||
@type conn :: %Plug.Conn{} | ||
|
||
@spec get_header(conn(), header_name()) :: {:error, :notfound} | {:ok, header_value()} | ||
def get_header(conn, header_name) do | ||
case get_req_header(conn, header_name) do | ||
[] -> | ||
{:error, :notfound} | ||
|
||
[header] -> | ||
{:ok, header} | ||
end | ||
end | ||
|
||
@spec get_auth_header(conn()) :: {:error, :nocreds} | {:ok, header_value()} | ||
def get_auth_header(conn) do | ||
case get_header(conn, "authorization") do | ||
{:error, :notfound} -> {:error, :nocreds} | ||
{:ok, header} -> {:ok, header |> String.split() |> List.last()} | ||
end | ||
end | ||
|
||
@spec all_headers(conn()) :: {:ok, headers_map()} | ||
def all_headers(conn) do | ||
{:ok, Enum.into(conn.req_headers, %{})} | ||
end | ||
end |
7 changes: 7 additions & 0 deletions
7
channel-bridge/apps/bridge_restapi/lib/bridge_api/rest/health/probe.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
defmodule BridgeApi.Rest.Health.Probe do | ||
@callback readiness() :: :ok | :error | ||
@callback liveness() :: :ok | :error | ||
|
||
def liveness, do: :ok | ||
def readiness, do: :ok | ||
end |
4 changes: 4 additions & 0 deletions
4
channel-bridge/apps/bridge_restapi/lib/bridge_api/rest/prometheus_exporter.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
defmodule BridgeApi.Rest.PrometheusExporter do | ||
@moduledoc false | ||
use Prometheus.PlugExporter | ||
end |
Oops, something went wrong.