Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OIDC #374

Merged
merged 16 commits into from
Jul 15, 2023
Merged
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
1 change: 1 addition & 0 deletions .github/workflows/beta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:

jobs:
docker:
name: beta
runs-on: ubuntu-latest
steps:
- name: Set env
Expand Down
77 changes: 2 additions & 75 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,10 @@ on:
branches:
- '**'

env:
MIX_ENV: test
APP_URL: http://localhost:4000
DB_HOSTNAME_TEST: localhost
DB_USERNAME: my_user
DB_PASSWORD: my_password
DB_DATABASE: my_database
RADARR_BASE_URL: http://localhost:7878
RADARR_API_KEY: d031e8c9b9df4b2fab311d1c3b3fa2c5
SONARR_BASE_URL: http://localhost:8989
SONARR_API_KEY: 1accda4476394bfcaddefe8c4fd77d4a
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

jobs:

build:
name: build
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -40,64 +27,4 @@ jobs:
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64

test:
name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
runs-on: ubuntu-latest
strategy:
matrix:
otp: [ '24' ]
elixir: [ '1.14.3' ]

services:
postgres-test:
image: postgres
env:
POSTGRES_USER: my_user
POSTGRES_PASSWORD: my_password
POSTGRES_DB: my_database
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

steps:
- uses: actions/checkout@v2
- uses: FedericoCarboni/setup-ffmpeg@v2

# Setup library
- run: sudo cp -r ${GITHUB_WORKSPACE}/dev/library /library

# Setup Radarr
- run: chmod u+x .github/install-radarr.sh
- run: ./.github/install-radarr.sh
- run: curl --head -X GET --retry 5 --retry-connrefused --retry-delay 5 "http://localhost:7878"

# Seed Radarr
- run: curl -d "@${GITHUB_WORKSPACE}/dev/radarr/root-folder.json" -H "Content-Type:application/json" -X POST "http://localhost:7878/api/v3/rootfolder?apiKey=d031e8c9b9df4b2fab311d1c3b3fa2c5"
- run: curl -d "@${GITHUB_WORKSPACE}/dev/radarr/movies.json" -H "Content-Type:application/json" -X POST "http://localhost:7878/api/v3/movie/import?apiKey=d031e8c9b9df4b2fab311d1c3b3fa2c5"

# Setup Sonarr
- run: chmod u+x .github/install-sonarr.sh
- run: ./.github/install-sonarr.sh
- run: curl --head -X GET --retry 5 --retry-connrefused --retry-delay 5 "http://localhost:8989"

# Seed Sonarr
- run: curl -d "@${GITHUB_WORKSPACE}/dev/sonarr/root-folder.json" -H "Content-Type:application/json" -X POST "http://localhost:8989/api/v3/rootfolder?apikey=1accda4476394bfcaddefe8c4fd77d4a"
- run: curl -d "@${GITHUB_WORKSPACE}/dev/sonarr/series.json" -H "Content-Type:application/json" -X POST "http://localhost:8989/api/v3/series/import?apikey=1accda4476394bfcaddefe8c4fd77d4a"

# Run tests
- uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}
- run: mix deps.get
- run: mix ecto.migrate
- run: mix run priv/repo/seeds.exs
- run: mix test
- run: chmod u+x code-coverage.sh
- run: ./code-coverage.sh
platforms: linux/amd64,linux/arm64
3 changes: 2 additions & 1 deletion .github/workflows/tag.yml → .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Tag
name: Release

on:
push:
Expand All @@ -7,6 +7,7 @@ on:

jobs:
docker:
name: release
runs-on: ubuntu-latest
steps:
- name: Set env
Expand Down
96 changes: 96 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Test

on:
push:
branches:
- master
pull_request:
branches:
- '**'

env:
MIX_ENV: test
APP_URL: http://localhost:4000

DB_HOSTNAME_TEST: localhost
DB_USERNAME: my_user
DB_PASSWORD: my_password
DB_DATABASE: my_database

OAUTH_CLIENT_ID: someClientId
OAUTH_CLIENT_SECRET: someClientSecret
OAUTH_ISSUER_URL: http://localhost:8081
OAUTH_AUTHORIZE_URL: http://localhost:8081/authorize
OAUTH_TOKEN_URL: http://localhost:8081/token
OAUTH_REDIRECT_URI: http://localhost:8081/auth/callback
OAUTH_USER_URL: http://localhost:8081/user

RADARR_BASE_URL: http://localhost:7878
RADARR_API_KEY: d031e8c9b9df4b2fab311d1c3b3fa2c5
SONARR_BASE_URL: http://localhost:8989
SONARR_API_KEY: 1accda4476394bfcaddefe8c4fd77d4a

GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

jobs:

test:
name: test
runs-on: ubuntu-latest
strategy:
matrix:
otp: [ '24' ]
elixir: [ '1.14.3' ]

services:
postgres-test:
image: postgres
env:
POSTGRES_USER: my_user
POSTGRES_PASSWORD: my_password
POSTGRES_DB: my_database
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

steps:
- uses: actions/checkout@v2
- uses: FedericoCarboni/setup-ffmpeg@v2

# Setup library
- run: sudo cp -r ${GITHUB_WORKSPACE}/dev/library /library

# Setup Radarr
- run: chmod u+x .github/install-radarr.sh
- run: ./.github/install-radarr.sh
- run: curl --head -X GET --retry 5 --retry-connrefused --retry-delay 5 "http://localhost:7878"

# Seed Radarr
- run: curl -d "@${GITHUB_WORKSPACE}/dev/radarr/root-folder.json" -H "Content-Type:application/json" -X POST "http://localhost:7878/api/v3/rootfolder?apiKey=d031e8c9b9df4b2fab311d1c3b3fa2c5"
- run: curl -d "@${GITHUB_WORKSPACE}/dev/radarr/movies.json" -H "Content-Type:application/json" -X POST "http://localhost:7878/api/v3/movie/import?apiKey=d031e8c9b9df4b2fab311d1c3b3fa2c5"

# Setup Sonarr
- run: chmod u+x .github/install-sonarr.sh
- run: ./.github/install-sonarr.sh
- run: curl --head -X GET --retry 5 --retry-connrefused --retry-delay 5 "http://localhost:8989"

# Seed Sonarr
- run: curl -d "@${GITHUB_WORKSPACE}/dev/sonarr/root-folder.json" -H "Content-Type:application/json" -X POST "http://localhost:8989/api/v3/rootfolder?apikey=1accda4476394bfcaddefe8c4fd77d4a"
- run: curl -d "@${GITHUB_WORKSPACE}/dev/sonarr/series.json" -H "Content-Type:application/json" -X POST "http://localhost:8989/api/v3/series/import?apikey=1accda4476394bfcaddefe8c4fd77d4a"

# Run tests
- uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}
- run: mix deps.get
- run: mix ecto.migrate
- run: mix run priv/repo/seeds.exs
- run: mix test
- run: chmod u+x code-coverage.sh
- run: ./code-coverage.sh
16 changes: 16 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
version: "3.4"

volumes:
database:
driver: local
redis:
driver: local

services:

midarr:
Expand All @@ -21,6 +29,14 @@ services:
- SETUP_ADMIN_NAME=admin
- SETUP_ADMIN_PASSWORD=passwordpassword

- OAUTH_CLIENT_ID=someClientId
- OAUTH_CLIENT_SECRET=someClientSecret
- OAUTH_ISSUER_URL=http://localhost:8081
- OAUTH_AUTHORIZE_URL=http://localhost:8081/authorize
- OAUTH_TOKEN_URL=http://localhost:8081/token
- OAUTH_REDIRECT_URI=http://localhost:8081/auth/callback
- OAUTH_USER_URL=http://localhost:8081/user

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@onedr0p Example environment variables for configuring oauth.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good so far.

- RADARR_BASE_URL=http://radarr:7878
- RADARR_API_KEY=d031e8c9b9df4b2fab311d1c3b3fa2c5
- SONARR_BASE_URL=http://sonarr:8989
Expand Down
1 change: 1 addition & 0 deletions lib/media_server/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule MediaServer.Application do
# Start a worker by calling: MediaServer.Worker.start_link(arg)
# {MediaServer.Worker, arg}
MediaServerWeb.Presence,
{DynamicSupervisor, name: MediaServer.DynamicSupervisor},
MediaServer.Token,
MediaServer.MoviesIndex,
MediaServer.MoviesWebhook,
Expand Down
20 changes: 20 additions & 0 deletions lib/media_server_web/controllers/oauth_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule MediaServerWeb.OAuthController do
use MediaServerWeb, :controller

def index(conn, _params) do
conn
|> redirect(external: MediaServerWeb.OAuth.authorize_url!)
end

def callback(conn, %{"code" => code}) do
client = MediaServerWeb.OAuth.get_token!(code: code)

if user = MediaServer.Accounts.get_user_by_email(MediaServerWeb.OAuth.get_user!(client).email) do

MediaServerWeb.UserAuth.log_in_user(conn, user)
end

conn
|> redirect(to: "/")
end
end
51 changes: 51 additions & 0 deletions lib/media_server_web/oauth.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
defmodule MediaServerWeb.OAuth do
use OAuth2.Strategy

alias OAuth2.Strategy.AuthCode

defp config do
[
strategy: __MODULE__,
client_id: System.get_env("OAUTH_CLIENT_ID"),
client_secret: System.get_env("OAUTH_CLIENT_SECRET"),
site: System.get_env("OAUTH_ISSUER_URL"),
authorize_url: System.get_env("OAUTH_AUTHORIZE_URL"),
token_url: System.get_env("OAUTH_TOKEN_URL"),
redirect_uri: System.get_env("OAUTH_REDIRECT_URI")
]
end

def client do
OAuth2.Client.new(config())
end

def authorize_url!(_params \\ []) do
OAuth2.Client.authorize_url!(client())
end

def authorize_url(client, params) do
AuthCode.authorize_url(client, params)
end

def get_token!(params \\ [], _headers \\ []) do
OAuth2.Client.get_token!(client(), params)
end

def get_token(client, params, headers) do
client
|> put_header("Accept", "application/json")
|> AuthCode.get_token(params, headers)
end

def get_user!(client) do
token = Map.get(client, :token) |> Map.get(:access_token) |> Jason.decode! |> Map.get("access_token")

%{body: user} = OAuth2.Client.get!(client, System.get_env("OAUTH_USER_URL"), [
{"authorization", "Bearer #{ token }"}
])

decoded_user = Jason.decode!(user)

%{name: decoded_user["name"], email: decoded_user["email"]}
end
end
3 changes: 3 additions & 0 deletions lib/media_server_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ defmodule MediaServerWeb.Router do

get "/login", UserSessionController, :new
post "/login", UserSessionController, :create

get "/auth", OAuthController, :index
get "/auth/callback", OAuthController, :callback
end

scope "/", MediaServerWeb do
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ defmodule MediaServer.MixProject do
{:cors_plug, "~> 3.0"},
{:scrivener, "~> 2.0"},
{:extitles, "~> 0.1.0"},
{:exstream, "~> 0.20.0"}
{:exstream, "~> 0.20.0"},
{:oauth2, "~> 2.0"}
]
end

Expand Down
4 changes: 4 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@
"httpoison": {:hex, :httpoison, "2.1.0", "655fd9a7b0b95ee3e9a3b535cf7ac8e08ef5229bab187fa86ac4208b122d934b", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "fc455cb4306b43827def4f57299b2d5ac8ac331cb23f517e734a4b78210a160c"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"},
"openid_connect": {:hex, :openid_connect, "0.2.2", "c05055363330deab39ffd89e609db6b37752f255a93802006d83b45596189c0b", [:mix], [{:httpoison, "~> 1.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "735769b6d592124b58edd0582554ce638524c0214cd783d8903d33357d74cc13"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.7.6", "61f0625af7c1d1923d582470446de29b008c0e07ae33d7a3859ede247ddaf59a", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "f6b4be7780402bb060cbc6e83f1b6d3f5673b674ba73cc4a7dd47db0322dfb88"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.2", "b21bd01fdeffcfe2fab49e4942aa938b6d3e89e93a480d4aee58085560a0bc0d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "70242edd4601d50b69273b057ecf7b684644c19ee750989fd555625ae4ce8f5d"},
Expand All @@ -58,6 +61,7 @@
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
"tesla": {:hex, :tesla, "1.7.0", "a62dda2f80d4f8a925eb7b8c5b78c461e0eb996672719fe1a63b26321a5f8b4e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2e64f01ebfdb026209b47bc651a0e65203fcff4ae79c11efb73c4852b00dc313"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
"websock": {:hex, :websock, "0.5.2", "b3c08511d8d79ed2c2f589ff430bd1fe799bb389686dafce86d28801783d8351", [:mix], [], "hexpm", "925f5de22fca6813dfa980fb62fd542ec43a2d1a1f83d2caec907483fe66ff05"},
"websock_adapter": {:hex, :websock_adapter, "0.5.3", "4908718e42e4a548fc20e00e70848620a92f11f7a6add8cf0886c4232267498d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "cbe5b814c1f86b6ea002b52dd99f345aeecf1a1a6964e209d208fb404d930d3d"},
Expand Down
20 changes: 20 additions & 0 deletions test/media_server_web/controllers/oauth_controller_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule MediaServerWeb.OauthControllerTest do
use MediaServerWeb.ConnCase

test "it should redirect", %{conn: conn} do
conn = get(conn, Routes.o_auth_path(conn, :index))
response = html_response(conn, 302)

assert response =~ "You are being"
assert response =~ "redirected"
assert response =~ "authorize?client_id="
end

test "it should callback", %{conn: conn} do
conn = get(conn, Routes.o_auth_path(conn, :callback, code: "code"))
response = html_response(conn, 302)

assert response =~ "You are being"
assert response =~ "redirected"
end
end
Loading