Skip to content

Commit 96589b5

Browse files
Support explicit foward proxy from oauth2 plugin
1 parent f413880 commit 96589b5

File tree

53 files changed

+730
-166
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+730
-166
lines changed

.github/workflows/test-authnz.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010
- 'deps/rabbitmq_auth_**'
1111
- 'deps/rabbitmq_management/src/**'
1212
- 'deps/rabbitmq_management/priv/**'
13-
- 'deps/rabbitmq_management/selenium/**'
13+
- 'selenium/**'
1414
- 'scripts/**'
1515
- .bazelrc
1616
- .bazelversion

deps/oauth2_client/include/types.hrl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020
}).
2121
-type openid_configuration() :: #openid_configuration{}.
2222

23+
-record(proxy_options, {
24+
host :: string(),
25+
port :: integer(),
26+
username :: option(string() | binary()),
27+
password :: option(string() | binary())
28+
}).
29+
-type proxy_options() :: #proxy_options{}.
30+
2331
-record(oauth_provider, {
2432
id :: oauth_provider_id(),
2533
issuer :: option(uri_string:uri_string()),
@@ -28,7 +36,8 @@
2836
authorization_endpoint :: option(uri_string:uri_string()),
2937
end_session_endpoint :: option(uri_string:uri_string()),
3038
jwks_uri :: option(uri_string:uri_string()),
31-
ssl_options :: option(list())
39+
ssl_options :: option(list()),
40+
proxy_options :: option(proxy_options())
3241
}).
3342

3443
-type query_list() :: [{unicode:chardata(), unicode:chardata() | true}].

deps/oauth2_client/src/oauth2_client.erl

Lines changed: 129 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
-export([get_access_token/2, get_expiration_time/1,
99
refresh_access_token/2,
1010
get_oauth_provider/1, get_oauth_provider/2,
11-
get_openid_configuration/2,
11+
get_openid_configuration/3,
1212
build_openid_discovery_endpoint/3,
1313
merge_openid_configuration/2,
1414
merge_oauth_provider/2,
1515
extract_ssl_options_as_list/1,
16-
format_ssl_options/1, format_oauth_provider/1, format_oauth_provider_id/1
16+
map_proxy_auth_to_httpc_option/1,
17+
map_proxy_to_httpc_option/1,
18+
map_ssl_options_to_httpc_option/1,
19+
map_timeout_to_httpc_option/1,
20+
format_ssl_options/1, format_oauth_provider/1, format_oauth_provider_id/1
1721
]).
1822

1923
-include("oauth2_client.hrl").
@@ -28,10 +32,11 @@ get_access_token(OAuthProvider, Request) ->
2832
Header = [],
2933
Type = ?CONTENT_URLENCODED,
3034
Body = build_access_token_request_body(Request),
31-
HTTPOptions = get_ssl_options_if_any(OAuthProvider) ++
32-
get_timeout_of_default(Request#access_token_request.timeout),
33-
Options = [],
34-
Response = httpc:request(post, {URL, Header, Type, Body}, HTTPOptions, Options),
35+
HTTPOptions =
36+
map_ssl_options_to_httpc_option(OAuthProvider#oauth_provider.ssl_options) ++
37+
map_timeout_to_httpc_option(Request#access_token_request.timeout),
38+
Response = http_post(URL, Header, Type, Body, HTTPOptions,
39+
OAuthProvider#oauth_provider.proxy_options),
3540
parse_access_token_response(Response).
3641

3742
-spec refresh_access_token(oauth_provider(), refresh_token_request()) ->
@@ -42,12 +47,36 @@ refresh_access_token(OAuthProvider, Request) ->
4247
Header = [],
4348
Type = ?CONTENT_URLENCODED,
4449
Body = build_refresh_token_request_body(Request),
45-
HTTPOptions = get_ssl_options_if_any(OAuthProvider) ++
46-
get_timeout_of_default(Request#refresh_token_request.timeout),
47-
Options = [],
48-
Response = httpc:request(post, {URL, Header, Type, Body}, HTTPOptions, Options),
50+
HTTPOptions =
51+
map_ssl_options_to_httpc_option(OAuthProvider#oauth_provider.ssl_options) ++
52+
map_timeout_to_httpc_option(Request#refresh_token_request.timeout),
53+
Response = http_post(URL, Header, Type, Body, HTTPOptions,
54+
OAuthProvider#oauth_provider.proxy_options),
4955
parse_access_token_response(Response).
5056

57+
http_post(URL, Header, Type, Body, HTTPOptions, ProxyOptions) ->
58+
case ProxyOptions of
59+
undefined -> httpc:request(post, {URL, Header, Type, Body}, HTTPOptions, []);
60+
_ ->
61+
case httpc:set_options(map_proxy_to_httpc_option(ProxyOptions)) of
62+
ok ->
63+
httpc:request(post, {URL, Header, Type, Body},
64+
HTTPOptions ++ map_proxy_auth_to_httpc_option(ProxyOptions), []);
65+
{error, _} = Error -> Error
66+
end
67+
end.
68+
http_get(URL, HTTPOptions, ProxyOptions) ->
69+
case ProxyOptions of
70+
undefined -> httpc:request(get, {URL, []}, HTTPOptions, []);
71+
_ ->
72+
case httpc:set_options(map_proxy_to_httpc_option(ProxyOptions)) of
73+
ok ->
74+
httpc:request(get, {URL, []},
75+
HTTPOptions ++ map_proxy_auth_to_httpc_option(ProxyOptions), []);
76+
{error, _} = Error -> Error
77+
end
78+
end.
79+
5180
append_paths(Path1, Path2) ->
5281
erlang:iolist_to_binary([Path1, Path2]).
5382

@@ -94,14 +123,17 @@ drop_trailing_path_separator(Path) when is_list(Path) ->
94123
end.
95124

96125
-spec get_openid_configuration(DiscoveryEndpoint :: uri_string:uri_string(),
97-
ssl:tls_option() | []) -> {ok, openid_configuration()} | {error, term()}.
98-
get_openid_configuration(DiscoverEndpoint, TLSOptions) ->
99-
rabbit_log:debug("get_openid_configuration from ~p (~p)", [DiscoverEndpoint,
100-
format_ssl_options(TLSOptions)]),
101-
Options = [],
102-
Response = httpc:request(get, {DiscoverEndpoint, []}, TLSOptions, Options),
126+
ssl:tls_option() | [], proxy_options() | undefined | 'none') ->
127+
{ok, openid_configuration()} | {error, term()}.
128+
get_openid_configuration(DiscoverEndpoint, TLSOptions, ProxyOptions) ->
129+
rabbit_log:debug("get_openid_configuration from ~p (~p) [~p]", [DiscoverEndpoint,
130+
format_ssl_options(TLSOptions), format_proxy_options(ProxyOptions)]),
131+
HTTPOptions =
132+
map_ssl_options_to_httpc_option(TLSOptions) ++
133+
map_timeout_to_httpc_option(?DEFAULT_HTTP_TIMEOUT),
134+
Response = http_get(DiscoverEndpoint, HTTPOptions, ProxyOptions),
103135
parse_openid_configuration_response(Response).
104-
136+
105137
-spec merge_openid_configuration(openid_configuration(), oauth_provider()) ->
106138
oauth_provider().
107139
merge_openid_configuration(OpenId, OAuthProvider0) ->
@@ -283,7 +315,9 @@ download_oauth_provider(OAuthProvider) ->
283315
undefined -> {error, {missing_oauth_provider_attributes, [issuer]}};
284316
URL ->
285317
rabbit_log:debug("Downloading oauth_provider using ~p ", [URL]),
286-
case get_openid_configuration(URL, get_ssl_options_if_any(OAuthProvider)) of
318+
case get_openid_configuration(URL,
319+
OAuthProvider#oauth_provider.ssl_options,
320+
OAuthProvider#oauth_provider.proxy_options) of
287321
{ok, OpenIdConfiguration} ->
288322
{ok, update_oauth_provider_endpoints_configuration(
289323
merge_openid_configuration(OpenIdConfiguration, OAuthProvider))};
@@ -395,8 +429,36 @@ lookup_root_oauth_provider() ->
395429
token_endpoint = get_env(token_endpoint),
396430
authorization_endpoint = get_env(authorization_endpoint),
397431
end_session_endpoint = get_env(end_session_endpoint),
398-
ssl_options = extract_ssl_options_as_list(Map)
432+
ssl_options = extract_ssl_options_as_list(Map),
433+
proxy_options = extract_proxy_options(Map)
399434
}.
435+
436+
-spec extract_proxy_options(#{atom() => any()}|list()) -> proxy_options() | undefined.
437+
extract_proxy_options(List) when is_list(List) ->
438+
case {proplists:get_value(proxy_host, List, undefined),
439+
proplists:get_value(proxy_port, List, 0)} of
440+
{undefined, _} -> undefined;
441+
{_, 0} -> undefined;
442+
{H, P} ->
443+
#proxy_options{
444+
host = H,
445+
port = P,
446+
username = proplists:get_value(proxy_username, List, undefined),
447+
password = proplists:get_value(proxy_password, List, undefined)
448+
}
449+
end;
450+
extract_proxy_options(Map) ->
451+
case {maps:get(proxy_host, Map, undefined), maps:get(proxy_port, Map, 0)} of
452+
{undefined, _} -> undefined;
453+
{_, 0} -> undefined;
454+
{H, P} ->
455+
#proxy_options{
456+
host = H,
457+
port = P,
458+
username = maps:get(proxy_username, Map, undefined),
459+
password = maps:get(proxy_password, Map, undefined)
460+
}
461+
end.
400462

401463
-spec extract_ssl_options_as_list(#{atom() => any()}) -> proplists:proplist().
402464
extract_ssl_options_as_list(Map) ->
@@ -522,17 +584,44 @@ append_extra_parameters(Request, QueryList) ->
522584
Params -> Params ++ QueryList
523585
end.
524586

525-
get_ssl_options_if_any(OAuthProvider) ->
526-
case OAuthProvider#oauth_provider.ssl_options of
587+
map_ssl_options_to_httpc_option(SslOptions) ->
588+
case SslOptions of
527589
undefined -> [];
528590
Options -> [{ssl, Options}]
529591
end.
530-
get_timeout_of_default(Timeout) ->
592+
593+
map_timeout_to_httpc_option(Timeout) ->
531594
case Timeout of
532595
undefined -> [{timeout, ?DEFAULT_HTTP_TIMEOUT}];
533596
Timeout -> [{timeout, Timeout}]
534597
end.
535598

599+
map_proxy_to_httpc_option(ProxyOptions) ->
600+
case ProxyOptions of
601+
undefined ->
602+
[];
603+
Proxy -> case {Proxy#proxy_options.host, Proxy#proxy_options.port} of
604+
{undefined, 0} -> [];
605+
{_, 0} -> [];
606+
{undefined, _} -> [];
607+
{Host, Port} ->
608+
P = {{Host, Port},[]},
609+
[{proxy, P}]
610+
end
611+
end.
612+
613+
map_proxy_auth_to_httpc_option(ProxyOptions) ->
614+
case ProxyOptions of
615+
undefined ->
616+
[];
617+
Proxy ->
618+
case {Proxy#proxy_options.username, Proxy#proxy_options.password} of
619+
{undefined, _} -> [];
620+
{_, undefined} -> [];
621+
{_, _} = Auth -> [{proxy_auth, Auth}]
622+
end
623+
end.
624+
536625
is_json(?CONTENT_JSON) -> true;
537626
is_json(_) -> false.
538627

@@ -588,7 +677,9 @@ map_to_oauth_provider(PropList) when is_list(PropList) ->
588677
proplists:get_value(jwks_uri, PropList, undefined),
589678
ssl_options =
590679
extract_ssl_options_as_list(maps:from_list(
591-
proplists:get_value(https, PropList, [])))
680+
proplists:get_value(https, PropList, []))),
681+
proxy_options =
682+
extract_proxy_options(PropList)
592683
}.
593684
map_to_access_token_response(Code, Reason, Headers, Body) ->
594685
case decode_body(proplists:get_value("content-type", Headers, ?CONTENT_JSON), Body) of
@@ -626,6 +717,18 @@ format_ssl_options(TlsOptions) ->
626717
proplists:get_value(cacertfile, TlsOptions),
627718
CaCertsCount])).
628719

720+
-spec format_proxy_options(proxy_options()|undefined) -> string().
721+
format_proxy_options(undefined) ->
722+
lists:flatten(io_lib:format("{no proxy}", []));
723+
724+
format_proxy_options(ProxyOptions) ->
725+
lists:flatten(io_lib:format("{host: ~p, port: ~p, username: ~p, " ++
726+
"password: ~p }", [
727+
ProxyOptions#proxy_options.host,
728+
ProxyOptions#proxy_options.port,
729+
ProxyOptions#proxy_options.username,
730+
ProxyOptions#proxy_options.password])).
731+
629732
format_oauth_provider_id(root) -> "<from keyconfig>";
630733
format_oauth_provider_id(Id) -> binary_to_list(Id).
631734

@@ -634,15 +737,16 @@ format_oauth_provider(OAuthProvider) ->
634737
lists:flatten(io_lib:format("{id: ~p, issuer: ~p, discovery_endpoint: ~p, " ++
635738
" token_endpoint: ~p, " ++
636739
"authorization_endpoint: ~p, end_session_endpoint: ~p, " ++
637-
"jwks_uri: ~p, ssl_options: ~p }", [
740+
"jwks_uri: ~p, ssl_options: ~p, proxy_options: ~p}", [
638741
format_oauth_provider_id(OAuthProvider#oauth_provider.id),
639742
OAuthProvider#oauth_provider.issuer,
640743
OAuthProvider#oauth_provider.discovery_endpoint,
641744
OAuthProvider#oauth_provider.token_endpoint,
642745
OAuthProvider#oauth_provider.authorization_endpoint,
643746
OAuthProvider#oauth_provider.end_session_endpoint,
644747
OAuthProvider#oauth_provider.jwks_uri,
645-
format_ssl_options(OAuthProvider#oauth_provider.ssl_options)])).
748+
format_ssl_options(OAuthProvider#oauth_provider.ssl_options),
749+
format_proxy_options(OAuthProvider#oauth_provider.proxy_options)])).
646750

647751
get_env(Par) ->
648752
application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined).

deps/oauth2_client/test/system_SUITE.erl

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,11 @@ build_openid_discovery_endpoint(Issuer, Path) ->
322322

323323
get_openid_configuration(Config) ->
324324
ExpectedOAuthProvider = ?config(oauth_provider, Config),
325-
SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}],
325+
SslOptions = ExpectedOAuthProvider#oauth_provider.ssl_options,
326326
{ok, ActualOpenId} = oauth2_client:get_openid_configuration(
327327
build_openid_discovery_endpoint(build_issuer("https")),
328-
SslOptions),
328+
SslOptions,
329+
ExpectedOAuthProvider#oauth_provider.proxy_options),
329330
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
330331
assertOpenIdConfiguration(ExpectedOpenId, ActualOpenId).
331332

@@ -344,35 +345,39 @@ get_openid_configuration_returns_partial_payload(Config) ->
344345
token_endpoint = ExpectedOAuthProvider0#oauth_provider.token_endpoint,
345346
jwks_uri = ExpectedOAuthProvider0#oauth_provider.jwks_uri},
346347

347-
SslOptions = [{ssl, ExpectedOAuthProvider0#oauth_provider.ssl_options}],
348+
SslOptions = ExpectedOAuthProvider0#oauth_provider.ssl_options,
348349
{ok, Actual} = oauth2_client:get_openid_configuration(
349350
build_openid_discovery_endpoint(build_issuer("https")),
350-
SslOptions),
351+
SslOptions,
352+
ExpectedOAuthProvider0#oauth_provider.proxy_options),
351353
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
352354
assertOpenIdConfiguration(ExpectedOpenId, Actual).
353355

354356
get_openid_configuration_using_path(Config) ->
355357
ExpectedOAuthProvider = ?config(oauth_provider, Config),
356-
SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}],
358+
SslOptions = ExpectedOAuthProvider#oauth_provider.ssl_options,
357359
{ok, Actual} = oauth2_client:get_openid_configuration(
358360
build_openid_discovery_endpoint(build_issuer("https", ?ISSUER_PATH)),
359-
SslOptions),
361+
SslOptions,
362+
ExpectedOAuthProvider#oauth_provider.proxy_options),
360363
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
361364
assertOpenIdConfiguration(ExpectedOpenId,Actual).
362365
get_openid_configuration_using_path_and_custom_endpoint(Config) ->
363366
ExpectedOAuthProvider = ?config(oauth_provider, Config),
364-
SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}],
367+
SslOptions = ExpectedOAuthProvider#oauth_provider.ssl_options,
365368
{ok, Actual} = oauth2_client:get_openid_configuration(
366369
build_openid_discovery_endpoint(build_issuer("https", ?ISSUER_PATH),
367-
?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions),
370+
?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions,
371+
ExpectedOAuthProvider#oauth_provider.proxy_options),
368372
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
369373
assertOpenIdConfiguration(ExpectedOpenId, Actual).
370374
get_openid_configuration_using_custom_endpoint(Config) ->
371375
ExpectedOAuthProvider = ?config(oauth_provider, Config),
372-
SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}],
376+
SslOptions = ExpectedOAuthProvider#oauth_provider.ssl_options,
373377
{ok, Actual} = oauth2_client:get_openid_configuration(
374378
build_openid_discovery_endpoint(build_issuer("https"),
375-
?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions),
379+
?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions,
380+
ExpectedOAuthProvider#oauth_provider.proxy_options),
376381
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
377382
assertOpenIdConfiguration(ExpectedOpenId, Actual).
378383

deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,26 @@
220220
rabbit_oauth2_schema:translate_oauth_providers(Conf)
221221
end}.
222222

223+
{mapping,
224+
"auth_oauth2.proxy.host",
225+
"rabbitmq_auth_backend_oauth2.proxy.host",
226+
[{datatype, string}]}.
227+
228+
{mapping,
229+
"auth_oauth2.proxy.port",
230+
"rabbitmq_auth_backend_oauth2.proxy.port",
231+
[{datatype, integer}]}.
232+
233+
{mapping,
234+
"auth_oauth2.proxy.username",
235+
"rabbitmq_auth_backend_oauth2.proxy.username",
236+
[{datatype, string}]}.
237+
238+
{mapping,
239+
"auth_oauth2.proxy.password",
240+
"rabbitmq_auth_backend_oauth2.proxy.password",
241+
[{datatype, string}]}.
242+
223243
{mapping,
224244
"auth_oauth2.https.peer_verification",
225245
"rabbitmq_auth_backend_oauth2.key_config.peer_verification",
@@ -322,6 +342,26 @@
322342
"rabbitmq_auth_backend_oauth2.oauth_providers",
323343
[{datatype, integer}]}.
324344

345+
{mapping,
346+
"auth_oauth2.oauth_providers.$name.proxy.host",
347+
"rabbitmq_auth_backend_oauth2.oauth_providers",
348+
[{datatype, string}]}.
349+
350+
{mapping,
351+
"auth_oauth2.oauth_providers.$name.proxy.port",
352+
"rabbitmq_auth_backend_oauth2.oauth_providers",
353+
[{datatype, integer}]}.
354+
355+
{mapping,
356+
"auth_oauth2.oauth_providers.$name.proxy.username",
357+
"rabbitmq_auth_backend_oauth2.oauth_providers",
358+
[{datatype, string}]}.
359+
360+
{mapping,
361+
"auth_oauth2.oauth_providers.$name.proxy.password",
362+
"rabbitmq_auth_backend_oauth2.oauth_providers",
363+
[{datatype, string}]}.
364+
325365
{mapping,
326366
"auth_oauth2.oauth_providers.$name.https.hostname_verification",
327367
"rabbitmq_auth_backend_oauth2.oauth_providers",

0 commit comments

Comments
 (0)