%% ----------------------------------------------------------------------------
%% RabbitMQ OAuth2 Plugin
%%
%% See https://github.com/rabbitmq/rabbitmq-server/blob/master/deps/rabbitmq_auth_backend_oauth2/ for details.
%%
%% ----------------------------------------------------------------------------

%% OAuth Resource identity. Usage:
%% - This is the identity of a RabbitMQ server/cluster used as the
%% recipient of JWT Tokens (see audience claim, https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3).
%% - This is also the resource identifier used by RabbitMQ server/cluster in the authorization and access token
%% requests (https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-indicators-05#page-3)
%%
%% Up to version 3.12, RabbitMQ's scopes followed this pattern : <resource_server_id>.<scope>.
%% Nowadays, there is a new setting called scope_prefix and RabbitMQ's scopes follow this pattern instead:
%% <scope_prefix><scope>. Note that there is no dot in between.
%% The default value of this setting is `<resource_server_id>.`.
%% {resource_server_id, <<"my_rabbit_server">>},

{mapping,
 "auth_oauth2.resource_server_id",
 "rabbitmq_auth_backend_oauth2.resource_server_id",
 [{datatype, string}]}.

{translation,
 "rabbitmq_auth_backend_oauth2.resource_server_id",
 fun(Conf) -> list_to_binary(cuttlefish:conf_get("auth_oauth2.resource_server_id", Conf))
 end}.

%% A prefix used for scopes to avoid scope collisions (or unintended overlap). If not configured,
%% it is defaulted to `<resource_server_id>.` to maintain backward compatibility. Empty string is a permitted value.
%%
%% {scope_prefix, <<"api:/rabbitmq:">>},

{mapping,
 "auth_oauth2.scope_prefix",
 "rabbitmq_auth_backend_oauth2.scope_prefix",
 [{datatype, string}]}.

{translation,
 "rabbitmq_auth_backend_oauth2.scope_prefix",
 fun(Conf) -> list_to_binary(cuttlefish:conf_get("auth_oauth2.scope_prefix", Conf))
 end}.

%% An identifier used for JWT Tokens compliant with Rich Authorization Request spec
%% RabbitMq uses this field as discriminator to filter out permissions meant for RabbitMQ
%% as a Resource server
%%
%% {resource_server_type, <<"rabbitmq">>},

{mapping,
 "auth_oauth2.resource_server_type",
 "rabbitmq_auth_backend_oauth2.resource_server_type",
 [{datatype, string}]}.

{translation,
 "rabbitmq_auth_backend_oauth2.resource_server_type",
 fun(Conf) -> list_to_binary(cuttlefish:conf_get("auth_oauth2.resource_server_type", Conf))
 end}.

%% Configure the plugin to also look in other fields using additional_scopes_key (maps to extra_scopes_source in the old format)
%%
%% {additional_scopes_key, <<"my_custom_scope_key">>},

{mapping,
 "auth_oauth2.additional_scopes_key",
 "rabbitmq_auth_backend_oauth2.extra_scopes_source",
 [{datatype, string}]}.

{translation,
 "rabbitmq_auth_backend_oauth2.extra_scopes_source",
 fun(Conf) ->
    list_to_binary(cuttlefish:conf_get("auth_oauth2.additional_scopes_key", Conf))
 end}.

{mapping,
 "auth_oauth2.scope_aliases.$alias",
 "rabbitmq_auth_backend_oauth2.scope_aliases",
 [{datatype, string}]}.

{mapping,
 "auth_oauth2.scope_aliases.$index.alias",
 "rabbitmq_auth_backend_oauth2.scope_aliases",
 [{datatype, string}]}.

{mapping,
 "auth_oauth2.scope_aliases.$index.scope",
 "rabbitmq_auth_backend_oauth2.scope_aliases",
 [{datatype, string}]}.

{translation,
 "rabbitmq_auth_backend_oauth2.scope_aliases",
 fun(Conf) ->
    rabbit_oauth2_schema:translate_scope_aliases(Conf)
 end}.

%% Configure the plugin to skip validation of the aud field
%%
%% {verify_aud, true},

{mapping,
 "auth_oauth2.verify_aud",
 "rabbitmq_auth_backend_oauth2.verify_aud",
 [{datatype, boolean}]}.

%% Configure the preferred username's JWT claim(s). These are the JWT claims or attributes
%% used to determine the user's identity, a.k.a username.
%% RabbitMQ appends `sub` and `client_id` claims.
%% e.g. RabbitMQ first looks for username claim. If it is not present, it looks for user_name
%% and so forth. If it cannot find any, it defaults to unknown.
%% {preferred_username_claims, [<<"username">>, <<"user_name">>, <<"email">> ]},

{mapping,
 "auth_oauth2.preferred_username_claims.$preferred_username_claims",
 "rabbitmq_auth_backend_oauth2.preferred_username_claims",
 [{datatype, string}]}.

{translation,
 "rabbitmq_auth_backend_oauth2.preferred_username_claims",
 fun(Conf) ->
    Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.preferred_username_claims", Conf),
    [list_to_binary(V) || {_, V} <- lists:reverse(Settings)]
 end}.



%% ID of the default signing key
%%
%% {default_key, <<"key-1">>},

{mapping,
 "auth_oauth2.default_key",
 "rabbitmq_auth_backend_oauth2.key_config.default_key",
 [{datatype, string}]}.

{translation,
 "rabbitmq_auth_backend_oauth2.key_config.default_key",
 fun(Conf) -> list_to_binary(cuttlefish:conf_get("auth_oauth2.default_key", Conf)) end}.

%% A map of signing keys
%%
%% {signing_keys, #{<<"id1">> => {pem, <<"value1">>}, <<"id2">> => {pem, <<"value2">>}}}
%% validator doesn't work

{mapping,
 "auth_oauth2.signing_keys.$name",
 "rabbitmq_auth_backend_oauth2.key_config.signing_keys",
 [{datatype, file}, {validators, ["pem_file"]}]}.

{translation,
 "rabbitmq_auth_backend_oauth2.key_config.signing_keys",
 fun(Conf) ->
    rabbit_oauth2_schema:translate_signing_keys(Conf)
 end}.

{mapping,
 "auth_oauth2.issuer",
 "rabbitmq_auth_backend_oauth2.issuer",
 [{datatype, string}, {validators, ["uri", "https_uri"]}]}.

{mapping,
 "auth_oauth2.token_endpoint",
 "rabbitmq_auth_backend_oauth2.token_endpoint",
 [{datatype, string}, {validators, ["uri", "https_uri"]}]}.

%% Deprecates and replaces auth_oauth2.jwks_url
{mapping,
 "auth_oauth2.jwks_uri",
 "rabbitmq_auth_backend_oauth2.jwks_uri",
 [{datatype, string}, {validators, ["uri", "https_uri"]}]}.

%% DEPRECATED
{mapping,
 "auth_oauth2.jwks_url",
 "rabbitmq_auth_backend_oauth2.key_config.jwks_url",
 [{datatype, string}, {validators, ["uri", "https_uri"]}]}.

{mapping,
 "auth_oauth2.end_session_endpoint",
 "rabbitmq_auth_backend_oauth2.end_session_endpoint",
 [{datatype, string}, {validators, ["uri", "https_uri"]}]}.

{mapping,
 "auth_oauth2.authorization_endpoint",
 "rabbitmq_auth_backend_oauth2.authorization_endpoint",
 [{datatype, string}, {validators, ["uri", "https_uri"]}]}.

{mapping,
 "auth_oauth2.discovery_endpoint_path",
 "rabbitmq_auth_backend_oauth2.discovery_endpoint_path",
 [{datatype, string}]}.

{mapping,
 "auth_oauth2.discovery_endpoint_params.$param",
 "rabbitmq_auth_backend_oauth2.discovery_endpoint_params",
 [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_oauth2.discovery_endpoint_params",
 fun(Conf) ->
    rabbit_oauth2_schema:translate_endpoint_params("discovery_endpoint_params", Conf)
 end}.

{mapping,
 "auth_oauth2.oauth_providers.$name.discovery_endpoint_params.$param",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, string}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.discovery_endpoint_path",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, string}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.algorithms.$algorithm",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_oauth2.oauth_providers",
 fun(Conf) ->
    rabbit_oauth2_schema:translate_oauth_providers(Conf)
 end}.

{mapping,
 "auth_oauth2.https.peer_verification",
 "rabbitmq_auth_backend_oauth2.key_config.peer_verification",
 [{datatype, {enum, [verify_peer, verify_none]}}]}.

% Alias configuration variable. `auth_oauth2.https.peer_verification` will be soon deprecated
{mapping,
 "auth_oauth2.https.verify",
 "rabbitmq_auth_backend_oauth2.key_config.verify",
 [{datatype, {enum, [verify_peer, verify_none]}}]}.

{mapping,
 "auth_oauth2.https.cacertfile",
 "rabbitmq_auth_backend_oauth2.key_config.cacertfile",
 [{datatype, file}, {validators, ["pem_file"]}]}.

{mapping,
 "auth_oauth2.https.depth",
 "rabbitmq_auth_backend_oauth2.key_config.depth",
 [{datatype, integer}]}.

{mapping,
 "auth_oauth2.https.hostname_verification",
 "rabbitmq_auth_backend_oauth2.key_config.hostname_verification",
 [{datatype, {enum, [wildcard, none]}}]}.

{mapping,
 "auth_oauth2.https.crl_check",
 "rabbitmq_auth_backend_oauth2.key_config.crl_check",
 [{datatype, {enum, [true, false, peer, best_effort]}}]}.

{mapping,
 "auth_oauth2.https.fail_if_no_peer_cert",
 "rabbitmq_auth_backend_oauth2.key_config.fail_if_no_peer_cert",
 [{datatype, boolean}]}.

{validator, "https_uri", "According to the JWT Specification, Key Server URL must be https.",
 fun(Uri) -> string:nth_lexeme(Uri, 1, "://") == "https" end}.

%% No-op stub: pre-3.8.0 cuttlefish silently ignored unknown validator names,
%% so call sites referencing "uri" performed no check. 3.8.0 rejects undefined
%% names; this preserves the original accept-all behaviour.
{validator, "uri", "URI string (no additional validation)",
 fun(_Uri) -> true end}.

{mapping,
 "auth_oauth2.algorithms.$algorithm",
 "rabbitmq_auth_backend_oauth2.key_config.algorithms",
 [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_oauth2.key_config.algorithms",
 fun(Conf) ->
     Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.algorithms", Conf),
     [list_to_binary(V) || {_, V} <- Settings]
 end}.


%% This setting is only required when there are multiple `auth_oauth2.oauth_providers`.
%% If this setting is omitted, its default to the first oauth_provider on the list.

{mapping,
  "auth_oauth2.default_oauth_provider",
  "rabbitmq_auth_backend_oauth2.default_oauth_provider",
  [{datatype, string}]}.

{mapping,
  "auth_oauth2.oauth_providers.$name.issuer",
  "rabbitmq_auth_backend_oauth2.oauth_providers",
  [{datatype, string}, {validators, ["uri", "https_uri"]}]
}.

{mapping,
  "auth_oauth2.oauth_providers.$name.token_endpoint",
  "rabbitmq_auth_backend_oauth2.oauth_providers",
  [{datatype, string}, {validators, ["uri", "https_uri"]}]
}.

{mapping,
 "auth_oauth2.oauth_providers.$name.jwks_uri",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, string}, {validators, ["uri", "https_uri"]}]
}.

{mapping,
 "auth_oauth2.oauth_providers.$name.end_session_endpoint",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, string}, {validators, ["uri", "https_uri"]}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.authorization_endpoint",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, string}, {validators, ["uri", "https_uri"]}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.https.verify",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, {enum, [verify_peer, verify_none]}}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.https.cacertfile",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, file}, {validators, ["pem_file"]}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.https.depth",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, integer}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.https.hostname_verification",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, {enum, [wildcard, none]}}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.https.crl_check",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, {enum, [true, false, peer, best_effort]}}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.default_key",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, string}]}.

%% A map of signing keys
%%
%% {signing_keys, #{<<"id1">> => {pem, <<"value1">>}, <<"id2">> => {pem, <<"value2">>}}}
%% validator doesn't work

{mapping,
 "auth_oauth2.oauth_providers.$name.signing_keys.$id",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, file}, {validators, ["pem_file"]}]}.

{mapping,
 "auth_oauth2.oauth_providers.$name.algorithms.$algorithm",
 "rabbitmq_auth_backend_oauth2.oauth_providers",
 [{datatype, string}]}.

{translation, "rabbitmq_auth_backend_oauth2.oauth_providers",
 fun(Conf) ->
    rabbit_oauth2_schema:translate_oauth_providers(Conf)
 end}.

{mapping,
  "auth_oauth2.resource_servers.$name.id",
  "rabbitmq_auth_backend_oauth2.resource_servers",
  [{datatype, string}]
}.


{mapping,
  "auth_oauth2.resource_servers.$name.scope_prefix",
  "rabbitmq_auth_backend_oauth2.resource_servers",
  [{datatype, string}]
}.

{mapping,
  "auth_oauth2.resource_servers.$name.additional_scopes_key",
  "rabbitmq_auth_backend_oauth2.resource_servers",
  [{datatype, string}]
}.

{mapping,
  "auth_oauth2.resource_servers.$name.resource_server_type",
  "rabbitmq_auth_backend_oauth2.resource_servers",
  [{datatype, string}]
}.

{mapping,
 "auth_oauth2.resource_servers.$name.scope_aliases.$alias",
 "rabbitmq_auth_backend_oauth2.resource_servers",
 [{datatype, string}]}.

{mapping,
 "auth_oauth2.resource_servers.$name.scope_aliases.$index.alias",
 "rabbitmq_auth_backend_oauth2.resource_servers",
 [{datatype, string}]}.

{mapping,
 "auth_oauth2.resource_servers.$name.scope_aliases.$index.scope",
 "rabbitmq_auth_backend_oauth2.resource_servers",
 [{datatype, string}]}.

{mapping,
  "auth_oauth2.resource_servers.$name.oauth_provider_id",
  "rabbitmq_auth_backend_oauth2.resource_servers",
  [{datatype, string}]
}.

{mapping,
 "auth_oauth2.resource_servers.$name.preferred_username_claims.$preferred_username_claims",
 "rabbitmq_auth_backend_oauth2.resource_servers",
 [{datatype, string}]}.


{translation, "rabbitmq_auth_backend_oauth2.resource_servers",
 fun(Conf) ->
    rabbit_oauth2_schema:translate_resource_servers(Conf)
 end}.
