diff --git a/config/config.exs b/config/config.exs index 64f3604..6720a5d 100644 --- a/config/config.exs +++ b/config/config.exs @@ -58,6 +58,11 @@ config :mv, max_rows: 1000 ] +# OIDC group → role sync (optional). Overridden in runtime.exs from ENV in production. +config :mv, :oidc_role_sync, + admin_group_name: nil, + groups_claim: "groups" + # Configures the endpoint config :mv, MvWeb.Endpoint, url: [host: "localhost"], diff --git a/config/runtime.exs b/config/runtime.exs index 06a2cd8..b0079ef 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -153,6 +153,11 @@ if config_env() == :prod do client_secret: client_secret, redirect_uri: System.get_env("OIDC_REDIRECT_URI") || default_redirect_uri + # OIDC group → Admin role sync (optional). Groups claim default "groups". + config :mv, :oidc_role_sync, + admin_group_name: System.get_env("OIDC_ADMIN_GROUP_NAME"), + groups_claim: System.get_env("OIDC_GROUPS_CLAIM") || "groups" + # Token signing secret from environment variable # This overrides the placeholder value set in prod.exs # Supports TOKEN_SIGNING_SECRET or TOKEN_SIGNING_SECRET_FILE for Docker secrets. diff --git a/lib/mv/oidc_role_sync_config.ex b/lib/mv/oidc_role_sync_config.ex new file mode 100644 index 0000000..493a435 --- /dev/null +++ b/lib/mv/oidc_role_sync_config.ex @@ -0,0 +1,24 @@ +defmodule Mv.OidcRoleSyncConfig do + @moduledoc """ + Runtime configuration for OIDC group → role sync (e.g. admin group → Admin role). + + Reads from Application config `:mv, :oidc_role_sync`: + - `:admin_group_name` – OIDC group name that maps to Admin role (optional; when nil, no sync). + - `:groups_claim` – JWT/user_info claim name for groups (default: `"groups"`). + + Set via ENV in production: OIDC_ADMIN_GROUP_NAME, OIDC_GROUPS_CLAIM (see config/runtime.exs). + """ + @doc "Returns the OIDC group name that maps to Admin role, or nil if not configured." + def oidc_admin_group_name do + get(:admin_group_name) + end + + @doc "Returns the JWT/user_info claim name for groups; defaults to \"groups\"." + def oidc_groups_claim do + get(:groups_claim) || "groups" + end + + defp get(key) do + Application.get_env(:mv, :oidc_role_sync, []) |> Keyword.get(key) + end +end diff --git a/test/mv/oidc_role_sync_config_test.exs b/test/mv/oidc_role_sync_config_test.exs new file mode 100644 index 0000000..b4664aa --- /dev/null +++ b/test/mv/oidc_role_sync_config_test.exs @@ -0,0 +1,49 @@ +defmodule Mv.OidcRoleSyncConfigTest do + @moduledoc """ + Tests for OIDC role sync configuration (OIDC_ADMIN_GROUP_NAME, OIDC_GROUPS_CLAIM). + """ + use ExUnit.Case, async: false + + alias Mv.OidcRoleSyncConfig + + describe "oidc_admin_group_name/0" do + test "returns nil when OIDC_ADMIN_GROUP_NAME is not configured" do + restore = put_config(admin_group_name: nil) + on_exit(restore) + + assert OidcRoleSyncConfig.oidc_admin_group_name() == nil + end + + test "returns configured admin group name when set" do + restore = put_config(admin_group_name: "mila-admin") + on_exit(restore) + + assert OidcRoleSyncConfig.oidc_admin_group_name() == "mila-admin" + end + end + + describe "oidc_groups_claim/0" do + test "returns default \"groups\" when OIDC_GROUPS_CLAIM is not configured" do + restore = put_config(groups_claim: nil) + on_exit(restore) + + assert OidcRoleSyncConfig.oidc_groups_claim() == "groups" + end + + test "returns configured claim name when OIDC_GROUPS_CLAIM is set" do + restore = put_config(groups_claim: "ak_groups") + on_exit(restore) + + assert OidcRoleSyncConfig.oidc_groups_claim() == "ak_groups" + end + end + + defp put_config(opts) do + current = Application.get_env(:mv, :oidc_role_sync, []) + Application.put_env(:mv, :oidc_role_sync, Keyword.merge(current, opts)) + + fn -> + Application.put_env(:mv, :oidc_role_sync, current) + end + end +end