Support explicit foward proxy from oauth2 plugin

This commit is contained in:
Marcial Rosales 2024-12-19 17:31:34 +01:00
parent 68de3fdb77
commit b5be85ffab
66 changed files with 1175 additions and 202 deletions

View File

@ -10,13 +10,8 @@ on:
- 'deps/rabbitmq_auth_**'
- 'deps/rabbitmq_management/src/**'
- 'deps/rabbitmq_management/priv/**'
- 'deps/rabbitmq_management/selenium/**'
- 'selenium/**'
- 'scripts/**'
- .bazelrc
- .bazelversion
- BUILD.*
- '*.bzl'
- '*.bazel'
- .github/workflows/test-authnz.yaml
pull_request:
paths:

View File

@ -12,11 +12,6 @@ on:
- 'deps/rabbitmq_management/priv/**'
- 'deps/rabbitmq_web_dispatch/src/**'
- 'scripts/**'
- .bazelrc
- .bazelversion
- BUILD.*
- '*.bzl'
- '*.bazel'
- 'selenium/**'
- .github/workflows/test-management-ui.yaml

View File

@ -20,6 +20,14 @@
}).
-type openid_configuration() :: #openid_configuration{}.
-record(proxy_options, {
host :: string(),
port :: integer(),
username :: option(string() | binary()),
password :: option(string() | binary())
}).
-type proxy_options() :: #proxy_options{}.
-record(oauth_provider, {
id :: oauth_provider_id(),
issuer :: option(uri_string:uri_string()),
@ -28,7 +36,8 @@
authorization_endpoint :: option(uri_string:uri_string()),
end_session_endpoint :: option(uri_string:uri_string()),
jwks_uri :: option(uri_string:uri_string()),
ssl_options :: option(list())
ssl_options :: option(list()),
proxy_options :: option(proxy_options())
}).
-type query_list() :: [{unicode:chardata(), unicode:chardata() | true}].

View File

@ -7,12 +7,17 @@
-module(oauth2_client).
-export([get_access_token/2, get_expiration_time/1,
refresh_access_token/2,
get_jwks/1,
get_oauth_provider/1, get_oauth_provider/2,
get_openid_configuration/2,
get_openid_configuration/1,
build_openid_discovery_endpoint/3,
merge_openid_configuration/2,
merge_oauth_provider/2,
extract_ssl_options_as_list/1,
map_proxy_auth_to_httpc_option/1,
map_proxy_to_httpc_option/1,
map_ssl_options_to_httpc_option/1,
map_timeout_to_httpc_option/1,
format_ssl_options/1, format_oauth_provider/1, format_oauth_provider_id/1
]).
@ -25,29 +30,82 @@ get_access_token(OAuthProvider, Request) ->
rabbit_log:debug("get_access_token using OAuthProvider:~p and client_id:~p",
[OAuthProvider, Request#access_token_request.client_id]),
URL = OAuthProvider#oauth_provider.token_endpoint,
Id = OAuthProvider#oauth_provider.id,
Header = [],
Type = ?CONTENT_URLENCODED,
Body = build_access_token_request_body(Request),
HTTPOptions = get_ssl_options_if_any(OAuthProvider) ++
get_timeout_of_default(Request#access_token_request.timeout),
Options = [],
Response = httpc:request(post, {URL, Header, Type, Body}, HTTPOptions, Options),
HTTPOptions =
map_ssl_options_to_httpc_option(OAuthProvider#oauth_provider.ssl_options) ++
map_timeout_to_httpc_option(Request#access_token_request.timeout),
Response = http_post(Id, URL, Header, Type, Body, HTTPOptions,
OAuthProvider#oauth_provider.proxy_options),
parse_access_token_response(Response).
-spec refresh_access_token(oauth_provider(), refresh_token_request()) ->
{ok, successful_access_token_response()} |
{error, unsuccessful_access_token_response() | any()}.
refresh_access_token(OAuthProvider, Request) ->
Id = OAuthProvider#oauth_provider.id,
URL = OAuthProvider#oauth_provider.token_endpoint,
Header = [],
Type = ?CONTENT_URLENCODED,
Body = build_refresh_token_request_body(Request),
HTTPOptions = get_ssl_options_if_any(OAuthProvider) ++
get_timeout_of_default(Request#refresh_token_request.timeout),
Options = [],
Response = httpc:request(post, {URL, Header, Type, Body}, HTTPOptions, Options),
HTTPOptions =
map_ssl_options_to_httpc_option(OAuthProvider#oauth_provider.ssl_options) ++
map_timeout_to_httpc_option(Request#refresh_token_request.timeout),
Response = http_post(Id, URL, Header, Type, Body, HTTPOptions,
OAuthProvider#oauth_provider.proxy_options),
parse_access_token_response(Response).
ensure_http_client_started(Id, ProxyOptions) ->
Profile = case Id of
root -> root;
_ -> binary_to_atom(Id)
end,
rabbit_log:debug("Starting http client ~p ...", [Profile]),
case inets:start(httpc, [{profile, Profile}]) of
{ok, _} ->
HttpProxyOptions = map_proxy_to_httpc_option(ProxyOptions),
case ensure_http_proxy_options_if_any(Profile, HttpProxyOptions) of
ok -> {ok, Profile};
{error, _} = Error -> Error
end;
{error, {already_started, _}} ->
{ok, Profile};
Error ->
rabbit_log:error("Failed to start httpc client: ~p", [Error]),
Error
end.
ensure_http_proxy_options_if_any(_Profile, []) ->
ok;
ensure_http_proxy_options_if_any(Profile, HttpProxyOptions) ->
case httpc:set_options(HttpProxyOptions, Profile) of
ok ->
rabbit_log:debug("Successfully set_options ~p on http client ~p",
[HttpProxyOptions, Profile]),
ok;
{error, _} = Error -> Error
end.
http_post(Id, URL, Header, Type, Body, HTTPOptions, ProxyOptions) ->
http_request(Id, post, {URL, Header, Type, Body}, HTTPOptions, ProxyOptions).
http_get(Id, URL, HTTPOptions, ProxyOptions) ->
http_request(Id, get, {URL, []}, HTTPOptions, ProxyOptions).
http_request(Id, Method, Payload, HTTPOptions, ProxyOptions) ->
case ensure_http_client_started(Id, ProxyOptions) of
{ok, Profile} ->
case ProxyOptions of
undefined ->
httpc:request(Method, Payload, HTTPOptions, [], Profile);
_ ->
httpc:request(Method, Payload,
HTTPOptions ++ map_proxy_auth_to_httpc_option(ProxyOptions),
[],
Profile)
end;
{error, _} = Error -> Error
end.
append_paths(Path1, Path2) ->
erlang:iolist_to_binary([Path1, Path2]).
@ -93,15 +151,28 @@ drop_trailing_path_separator(Path) when is_list(Path) ->
_ -> Path
end.
-spec get_openid_configuration(DiscoveryEndpoint :: uri_string:uri_string(),
ssl:tls_option() | []) -> {ok, openid_configuration()} | {error, term()}.
get_openid_configuration(DiscoverEndpoint, TLSOptions) ->
rabbit_log:debug("get_openid_configuration from ~p (~p)", [DiscoverEndpoint,
format_ssl_options(TLSOptions)]),
Options = [],
Response = httpc:request(get, {DiscoverEndpoint, []}, TLSOptions, Options),
-spec get_openid_configuration(oauth_provider()) -> {ok, openid_configuration()} | {error, term()}.
get_openid_configuration(#oauth_provider{id = Id, discovery_endpoint = Endpoint,
ssl_options = SslOptions, proxy_options = ProxyOptions}) ->
rabbit_log:debug("get_openid_configuration from ~p (~p) [~p]", [Endpoint,
format_ssl_options(SslOptions), format_proxy_options(ProxyOptions)]),
HTTPOptions =
map_ssl_options_to_httpc_option(SslOptions) ++
map_timeout_to_httpc_option(?DEFAULT_HTTP_TIMEOUT),
Response = http_get(Id, Endpoint, HTTPOptions, ProxyOptions),
parse_openid_configuration_response(Response).
-spec get_jwks(oauth_provider()) -> {ok, term()} | {error, term()}.
get_jwks(#oauth_provider{id = Id, jwks_uri = JwksUrl,
ssl_options = SslOptions, proxy_options = ProxyOptions}) ->
rabbit_log:debug("get_jwks from ~p (~p) [~p]", [JwksUrl,
format_ssl_options(SslOptions), format_proxy_options(ProxyOptions)]),
HTTPOptions =
map_ssl_options_to_httpc_option(SslOptions) ++
map_timeout_to_httpc_option(?DEFAULT_HTTP_TIMEOUT),
http_get(Id, JwksUrl, HTTPOptions, ProxyOptions).
-spec merge_openid_configuration(openid_configuration(), oauth_provider()) ->
oauth_provider().
merge_openid_configuration(OpenId, OAuthProvider0) ->
@ -283,7 +354,7 @@ download_oauth_provider(OAuthProvider) ->
undefined -> {error, {missing_oauth_provider_attributes, [issuer]}};
URL ->
rabbit_log:debug("Downloading oauth_provider using ~p ", [URL]),
case get_openid_configuration(URL, get_ssl_options_if_any(OAuthProvider)) of
case get_openid_configuration(OAuthProvider) of
{ok, OpenIdConfiguration} ->
{ok, update_oauth_provider_endpoints_configuration(
merge_openid_configuration(OpenIdConfiguration, OAuthProvider))};
@ -341,7 +412,6 @@ get_oauth_provider(OAuthProviderId, ListOfRequiredAttributes)
[OAuthProviderId, Error0]),
Error0;
Config ->
rabbit_log:debug("Found oauth_provider configuration ~p", [Config]),
OAuthProvider = map_to_oauth_provider(Config),
rabbit_log:debug("Resolved oauth_provider ~p", [format_oauth_provider(OAuthProvider)]),
case find_missing_attributes(OAuthProvider, ListOfRequiredAttributes) of
@ -395,9 +465,37 @@ lookup_root_oauth_provider() ->
token_endpoint = get_env(token_endpoint),
authorization_endpoint = get_env(authorization_endpoint),
end_session_endpoint = get_env(end_session_endpoint),
ssl_options = extract_ssl_options_as_list(Map)
ssl_options = extract_ssl_options_as_list(Map),
proxy_options = extract_proxy_options(get_env(proxy, []))
}.
-spec extract_proxy_options(#{atom() => any()}|list()) -> proxy_options() | undefined.
extract_proxy_options(List) when is_list(List) ->
case {proplists:get_value(host, List, undefined),
proplists:get_value(port, List, 0)} of
{undefined, _} -> undefined;
{_, 0} -> undefined;
{H, P} ->
#proxy_options{
host = H,
port = P,
username = proplists:get_value(username, List, undefined),
password = proplists:get_value(password, List, undefined)
}
end;
extract_proxy_options(Map) ->
case {maps:get(host, Map, undefined), maps:get(port, Map, 0)} of
{undefined, _} -> undefined;
{_, 0} -> undefined;
{H, P} ->
#proxy_options{
host = H,
port = P,
username = maps:get(username, Map, undefined),
password = maps:get(password, Map, undefined)
}
end.
-spec extract_ssl_options_as_list(#{atom() => any()}) -> proplists:proplist().
extract_ssl_options_as_list(Map) ->
{Verify, CaCerts, CaCertFile} = case get_verify_or_peer_verification(Map, verify_peer) of
@ -522,17 +620,38 @@ append_extra_parameters(Request, QueryList) ->
Params -> Params ++ QueryList
end.
get_ssl_options_if_any(OAuthProvider) ->
case OAuthProvider#oauth_provider.ssl_options of
map_ssl_options_to_httpc_option(SslOptions) ->
case SslOptions of
undefined -> [];
Options -> [{ssl, Options}]
end.
get_timeout_of_default(Timeout) ->
map_timeout_to_httpc_option(Timeout) ->
case Timeout of
undefined -> [{timeout, ?DEFAULT_HTTP_TIMEOUT}];
Timeout -> [{timeout, Timeout}]
end.
map_proxy_to_httpc_option(ProxyOptions) ->
case ProxyOptions of
undefined -> [];
Proxy -> case {Proxy#proxy_options.host, Proxy#proxy_options.port} of
{_, 0} -> [];
{Host, Port} -> P = {{Host, Port},[]},
[{proxy, P}]
end
end.
map_proxy_auth_to_httpc_option(ProxyOptions) ->
case ProxyOptions of
undefined -> [];
Proxy -> case {Proxy#proxy_options.username, Proxy#proxy_options.password} of
{undefined, _} -> [];
{_, undefined} -> [];
{_, _} = Auth -> [{proxy_auth, Auth}]
end
end.
is_json(?CONTENT_JSON) -> true;
is_json(_) -> false.
@ -543,10 +662,8 @@ is_json(_) -> false.
decode_body(_, []) -> [];
decode_body(?CONTENT_JSON, Body) ->
case rabbit_json:try_decode(rabbit_data_coercion:to_binary(Body)) of
{ok, Value} ->
Value;
{error, _} = Error ->
Error
{ok, Value} -> Value;
{error, _} = Error -> Error
end;
decode_body(MimeType, Body) ->
Items = string:split(MimeType, ";"),
@ -588,14 +705,14 @@ map_to_oauth_provider(PropList) when is_list(PropList) ->
proplists:get_value(jwks_uri, PropList, undefined),
ssl_options =
extract_ssl_options_as_list(maps:from_list(
proplists:get_value(https, PropList, [])))
proplists:get_value(https, PropList, []))),
proxy_options =
extract_proxy_options(proplists:get_value(proxy, PropList, []))
}.
map_to_access_token_response(Code, Reason, Headers, Body) ->
case decode_body(proplists:get_value("content-type", Headers, ?CONTENT_JSON), Body) of
{error, {error, InternalError}} ->
{error, InternalError};
{error, _} = Error ->
Error;
{error, {error, InternalError}} -> {error, InternalError};
{error, _} = Error -> Error;
Value ->
case Code of
200 -> {ok, map_to_successful_access_token_response(Value)};
@ -626,6 +743,18 @@ format_ssl_options(TlsOptions) ->
proplists:get_value(cacertfile, TlsOptions),
CaCertsCount])).
-spec format_proxy_options(proxy_options()|undefined) -> string().
format_proxy_options(undefined) ->
lists:flatten(io_lib:format("{no proxy}", []));
format_proxy_options(ProxyOptions) ->
lists:flatten(io_lib:format("{host: ~p, port: ~p, username: ~p, " ++
"password: ~p }", [
ProxyOptions#proxy_options.host,
ProxyOptions#proxy_options.port,
ProxyOptions#proxy_options.username,
ProxyOptions#proxy_options.password])).
format_oauth_provider_id(root) -> "<from keyconfig>";
format_oauth_provider_id(Id) -> binary_to_list(Id).
@ -634,7 +763,7 @@ format_oauth_provider(OAuthProvider) ->
lists:flatten(io_lib:format("{id: ~p, issuer: ~p, discovery_endpoint: ~p, " ++
" token_endpoint: ~p, " ++
"authorization_endpoint: ~p, end_session_endpoint: ~p, " ++
"jwks_uri: ~p, ssl_options: ~p }", [
"jwks_uri: ~p, ssl_options: ~p, proxy_options: ~p}", [
format_oauth_provider_id(OAuthProvider#oauth_provider.id),
OAuthProvider#oauth_provider.issuer,
OAuthProvider#oauth_provider.discovery_endpoint,
@ -642,7 +771,8 @@ format_oauth_provider(OAuthProvider) ->
OAuthProvider#oauth_provider.authorization_endpoint,
OAuthProvider#oauth_provider.end_session_endpoint,
OAuthProvider#oauth_provider.jwks_uri,
format_ssl_options(OAuthProvider#oauth_provider.ssl_options)])).
format_ssl_options(OAuthProvider#oauth_provider.ssl_options),
format_proxy_options(OAuthProvider#oauth_provider.proxy_options)])).
get_env(Par) ->
application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined).

View File

@ -322,13 +322,25 @@ build_openid_discovery_endpoint(Issuer, Path) ->
get_openid_configuration(Config) ->
ExpectedOAuthProvider = ?config(oauth_provider, Config),
SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}],
{ok, ActualOpenId} = oauth2_client:get_openid_configuration(
build_openid_discovery_endpoint(build_issuer("https")),
SslOptions),
ensure_discovery_endpoint(ExpectedOAuthProvider)),
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
assertOpenIdConfiguration(ExpectedOpenId, ActualOpenId).
ensure_issuer(OAuthProvider, IssuerURL) ->
OAuthProvider#oauth_provider{
issuer = IssuerURL
}.
ensure_discovery_endpoint(OAuthProvider) ->
OAuthProvider#oauth_provider{
discovery_endpoint = build_openid_discovery_endpoint(OAuthProvider#oauth_provider.issuer)
}.
ensure_discovery_endpoint(OAuthProvider, DiscoveryEndpointPath) ->
OAuthProvider#oauth_provider{
discovery_endpoint = build_openid_discovery_endpoint(
OAuthProvider#oauth_provider.issuer,
DiscoveryEndpointPath)
}.
map_oauth_provider_to_openid_configuration(OAuthProvider) ->
#openid_configuration{
issuer = OAuthProvider#oauth_provider.issuer,
@ -344,35 +356,30 @@ get_openid_configuration_returns_partial_payload(Config) ->
token_endpoint = ExpectedOAuthProvider0#oauth_provider.token_endpoint,
jwks_uri = ExpectedOAuthProvider0#oauth_provider.jwks_uri},
SslOptions = [{ssl, ExpectedOAuthProvider0#oauth_provider.ssl_options}],
{ok, Actual} = oauth2_client:get_openid_configuration(
build_openid_discovery_endpoint(build_issuer("https")),
SslOptions),
ensure_discovery_endpoint(ExpectedOAuthProvider0)),
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
assertOpenIdConfiguration(ExpectedOpenId, Actual).
get_openid_configuration_using_path(Config) ->
ExpectedOAuthProvider = ?config(oauth_provider, Config),
SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}],
{ok, Actual} = oauth2_client:get_openid_configuration(
build_openid_discovery_endpoint(build_issuer("https", ?ISSUER_PATH)),
SslOptions),
ensure_discovery_endpoint(
ensure_issuer(ExpectedOAuthProvider, build_issuer("https", ?ISSUER_PATH)))),
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
assertOpenIdConfiguration(ExpectedOpenId,Actual).
get_openid_configuration_using_path_and_custom_endpoint(Config) ->
ExpectedOAuthProvider = ?config(oauth_provider, Config),
SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}],
{ok, Actual} = oauth2_client:get_openid_configuration(
build_openid_discovery_endpoint(build_issuer("https", ?ISSUER_PATH),
?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions),
ensure_discovery_endpoint(
ensure_issuer(ExpectedOAuthProvider, build_issuer("https", ?ISSUER_PATH)),
?CUSTOM_OPENID_CONFIGURATION_ENDPOINT)),
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
assertOpenIdConfiguration(ExpectedOpenId, Actual).
get_openid_configuration_using_custom_endpoint(Config) ->
ExpectedOAuthProvider = ?config(oauth_provider, Config),
SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}],
{ok, Actual} = oauth2_client:get_openid_configuration(
build_openid_discovery_endpoint(build_issuer("https"),
?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions),
ensure_discovery_endpoint(ExpectedOAuthProvider, ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT)),
ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider),
assertOpenIdConfiguration(ExpectedOpenId, Actual).

View File

@ -220,6 +220,26 @@
rabbit_oauth2_schema:translate_oauth_providers(Conf)
end}.
{mapping,
"auth_oauth2.proxy.host",
"rabbitmq_auth_backend_oauth2.proxy.host",
[{datatype, string}]}.
{mapping,
"auth_oauth2.proxy.port",
"rabbitmq_auth_backend_oauth2.proxy.port",
[{datatype, integer}]}.
{mapping,
"auth_oauth2.proxy.username",
"rabbitmq_auth_backend_oauth2.proxy.username",
[{datatype, string}]}.
{mapping,
"auth_oauth2.proxy.password",
"rabbitmq_auth_backend_oauth2.proxy.password",
[{datatype, string}]}.
{mapping,
"auth_oauth2.https.peer_verification",
"rabbitmq_auth_backend_oauth2.key_config.peer_verification",
@ -322,6 +342,26 @@
"rabbitmq_auth_backend_oauth2.oauth_providers",
[{datatype, integer}]}.
{mapping,
"auth_oauth2.oauth_providers.$name.proxy.host",
"rabbitmq_auth_backend_oauth2.oauth_providers",
[{datatype, string}]}.
{mapping,
"auth_oauth2.oauth_providers.$name.proxy.port",
"rabbitmq_auth_backend_oauth2.oauth_providers",
[{datatype, integer}]}.
{mapping,
"auth_oauth2.oauth_providers.$name.proxy.username",
"rabbitmq_auth_backend_oauth2.oauth_providers",
[{datatype, string}]}.
{mapping,
"auth_oauth2.oauth_providers.$name.proxy.password",
"rabbitmq_auth_backend_oauth2.oauth_providers",
[{datatype, string}]}.
{mapping,
"auth_oauth2.oauth_providers.$name.https.hostname_verification",
"rabbitmq_auth_backend_oauth2.oauth_providers",

View File

@ -158,6 +158,7 @@ translate_oauth_providers(Conf) ->
Settings),
extract_oauth_providers_algorithm(Settings),
extract_oauth_providers_https(Settings),
extract_oauth_providers_proxy(Settings),
extract_oauth_providers_signing_keys(Settings)
]).
@ -264,6 +265,15 @@ mapOauthProviderProperty({Key, Value}) ->
_ -> Value
end}.
extract_oauth_providers_proxy(Settings) ->
ExtractProviderNameFun = fun extract_key_as_binary/1,
AttributesPerProvider = [{Name, mapProxyProperty({list_to_atom(Key), V})} ||
{[?AUTH_OAUTH2, ?OAUTH_PROVIDERS, Name, "proxy", Key], V} <- Settings ],
maps:map(fun(_K,V)-> [{proxy, V}] end,
maps:groups_from_list(ExtractProviderNameFun, fun({_, V}) -> V end,
AttributesPerProvider)).
extract_oauth_providers_https(Settings) ->
ExtractProviderNameFun = fun extract_key_as_binary/1,
@ -280,6 +290,9 @@ mapHttpProperty({Key, Value}) ->
_ -> Value
end}.
mapProxyProperty({Key, Value}) ->
{Key, Value}.
extract_oauth_providers_algorithm(Settings) ->
KeyFun = fun extract_key_as_binary/1,

View File

@ -1,7 +0,0 @@
-module(uaa_jwks).
-export([get/2]).
-spec get(uri_string:uri_string(), list()) -> {ok, term()} | {error, term()}.
get(JwksUrl, SslOptions) ->
Options = [{timeout, 60000}] ++ [{ssl, SslOptions}],
httpc:request(get, {JwksUrl, []}, Options, []).

View File

@ -42,11 +42,10 @@ add_signing_key(KeyId, Type, Value) ->
end.
-spec update_jwks_signing_keys(oauth_provider()) -> ok | {error, term()}.
update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl,
ssl_options = SslOptions}) ->
rabbit_log:debug("Downloading signing keys from ~tp (TLS options: ~p)",
[JwksUrl, format_ssl_options(SslOptions)]),
case uaa_jwks:get(JwksUrl, SslOptions) of
update_jwks_signing_keys(#oauth_provider{id = Id} = OAuthProvider) ->
rabbit_log:debug("Downloading signing keys from OauthProvider: ~tp",
[Id]),
case oauth2_client:get_jwks(OAuthProvider) of
{ok, {_, _, JwksBody}} ->
KeyList = maps:get(<<"keys">>,
jose:decode(erlang:iolist_to_binary(JwksBody)), []),

View File

@ -316,5 +316,37 @@
}
]}
], []
},
{proxy,
"auth_oauth2.proxy.host = localproxy
auth_oauth2.proxy.port = 8080
auth_oauth2.proxy.username = proxyuser
auth_oauth2.proxy.password = proxypwd
auth_oauth2.oauth_providers.keycloak.proxy.host = localproxy2
auth_oauth2.oauth_providers.keycloak.proxy.port = 8080
auth_oauth2.oauth_providers.keycloak.proxy.username = proxyuser2
auth_oauth2.oauth_providers.keycloak.proxy.password = proxypwd2",
[
{rabbitmq_auth_backend_oauth2, [
{proxy, [
{host, "localproxy"},
{password, "proxypwd"},
{port, 8080},
{username, "proxyuser"}
]},
{oauth_providers,
#{
<<"keycloak">> => [
{proxy, [
{password, "proxypwd2"},
{username, "proxyuser2"},
{port, 8080},
{host, "localproxy2"}
]}
]
}
}
]}
], []
}
].

View File

@ -66,6 +66,9 @@ verify_provider() -> [
{oauth_provider_with_issuer, [], [
get_oauth_provider_has_jwks_uri
]}
,{oauth_provider_with_proxy, [], [
get_oauth_provider_has_proxy
]}
].
init_per_suite(Config) ->
@ -149,6 +152,27 @@ init_per_group(with_resource_server_id, Config) ->
set_env(resource_server_id, ?RABBITMQ),
Config;
init_per_group(oauth_provider_with_proxy, Config) ->
Proxy = [
{host, "idp"},
{port, 8080},
{username, <<"user1">>},
{password, <<"pwd1">>}
],
case ?config(oauth_provider_id, Config) of
root ->
set_env(proxy, Proxy);
Id ->
OAuthProviders = get_env(oauth_providers, #{}),
OAuthProvider = maps:get(Id, OAuthProviders, []),
set_env(oauth_providers, maps:put(Id,
[{proxy, Proxy}] ++ OAuthProvider, OAuthProviders))
end,
[{proxy_hostname, "idp"},
{proxy_port, 8080},
{proxy_username, <<"user1">>},
{proxy_password, <<"pwd1">>}] ++ Config;
init_per_group(with_algorithms, Config) ->
KeyConfig = get_env(key_config, []),
set_env(key_config, KeyConfig ++ [{algorithms, [<<"HS256">>, <<"RS256">>]}]),
@ -190,6 +214,16 @@ init_per_group(_any, Config) ->
end_per_group(with_rabbitmq_node, Config) ->
rabbit_ct_helpers:run_steps(Config, rabbit_ct_broker_helpers:teardown_steps());
end_per_group(oauth_provider_with_proxy, Config) ->
case ?config(oauth_provider_id, Config) of
root ->
unset_env(proxy);
Id ->
unset_oauth_provider_properties(Id,
[host, port, username, password])
end,
Config;
end_per_group(with_root_static_signing_keys, Config) ->
KeyConfig = call_get_env(Config, key_config, []),
call_set_env(Config, key_config, KeyConfig),
@ -408,9 +442,21 @@ get_oauth_provider_with_jwks_uri_returns_error(Config) ->
get_oauth_provider_has_jwks_uri(Config) ->
{ok, OAuthProvider} = get_oauth_provider(
?config(oauth_provider_id, Config), [jwks_uri]),
ct:log("OAuthProvider: ~p", [OAuthProvider]),
?assertEqual(?config(jwks_uri, Config), OAuthProvider#oauth_provider.jwks_uri).
get_oauth_provider_has_proxy(Config) ->
{ok, OAuthProvider} = get_oauth_provider(
?config(oauth_provider_id, Config), [jwks_uri]),
?assertEqual(?config(proxy_port, Config),
OAuthProvider#oauth_provider.proxy_options#proxy_options.port),
?assertEqual(?config(proxy_hostname, Config),
OAuthProvider#oauth_provider.proxy_options#proxy_options.host),
?assertEqual(?config(proxy_username, Config),
OAuthProvider#oauth_provider.proxy_options#proxy_options.username),
?assertEqual(?config(proxy_password, Config),
OAuthProvider#oauth_provider.proxy_options#proxy_options.password).
%% ---- Utility functions

View File

@ -0,0 +1,46 @@
HTTPD_DOCKER_IMAGE=httpd:latest
ensure_devkeycloak-proxy() {
if docker ps | grep devkeycloak-proxy &> /dev/null; then
print "devkeycloak-proxy already running ..."
else
start_devkeycloak-proxy
fi
}
init_devkeycloak-proxy() {
HTTPD_CONFIG_DIR=${TEST_CONFIG_DIR}/devkeycloak-proxy
PROXY_HOSTNAME=devkeycloak-proxy
PROXY_PORT=9092
print "> HTTPD_CONFIG: ${HTTPD_CONFIG_DIR}"
print "> PROXY_HOSTNAME: ${PROXY_HOSTNAME}"
print "> PROXY_PORT: ${PROXY_PORT}"
}
start_devkeycloak-proxy() {
begin "Starting devkeycloak-proxy ..."
init_devkeycloak-proxy
kill_container_if_exist devkeycloak-proxy
MOUNT_HTTPD_CONFIG_DIR=$CONF_DIR/httpd
mkdir -p $MOUNT_HTTPD_CONFIG_DIR
${BIN_DIR}/gen-httpd-conf ${HTTPD_CONFIG_DIR} $ENV_FILE $MOUNT_HTTPD_CONFIG_DIR/httpd.conf
print "> EFFECTIVE HTTPD_CONFIG_FILE: $MOUNT_HTTPD_CONFIG_DIR/httpd.conf"
cp ${HTTPD_CONFIG_DIR}/.htpasswd $MOUNT_HTTPD_CONFIG_DIR
docker run \
--detach \
--name devkeycloak-proxy \
--net ${DOCKER_NETWORK} \
--publish 9092:9092 \
--mount "type=bind,source=${MOUNT_HTTPD_CONFIG_DIR},target=/usr/local/apache2/conf" \
${HTTPD_DOCKER_IMAGE}
#wait_for_url $OAUTH_PROVIDER_URL ${FORWARD_PROXY_URL}
wait_for_message devkeycloak-proxy "initializing worker proxy:forward local"
end "devkeycloak-proxy is ready"
}

View File

@ -0,0 +1,47 @@
HTTPD_DOCKER_IMAGE=httpd:latest
ensure_forward-proxy() {
if docker ps | grep forward-proxy &> /dev/null; then
print "forward-proxy already running ..."
else
start_forward-proxy
fi
}
init_forward-proxy() {
HTTPD_CONFIG_DIR=${TEST_CONFIG_DIR}/forward-proxy
PROXY_HOSTNAME=forward-proxy
PROXY_PORT=9092
print "> HTTPD_CONFIG: ${HTTPD_CONFIG_DIR}"
print "> OAUTH_PROVIDER_URL: ${OAUTH_PROVIDER_URL}"
print "> PROXY_HOSTNAME: ${PROXY_HOSTNAME}"
print "> PROXY_PORT: ${PROXY_PORT}"
}
start_forward-proxy() {
begin "Starting forward-proxy ..."
init_forward-proxy
kill_container_if_exist forward-proxy
MOUNT_HTTPD_CONFIG_DIR=$CONF_DIR/httpd
mkdir -p $MOUNT_HTTPD_CONFIG_DIR
${BIN_DIR}/gen-httpd-conf ${HTTPD_CONFIG_DIR} $ENV_FILE $MOUNT_HTTPD_CONFIG_DIR/httpd.conf
print "> EFFECTIVE HTTPD_CONFIG_FILE: $MOUNT_HTTPD_CONFIG_DIR/httpd.conf"
cp ${HTTPD_CONFIG_DIR}/.htpasswd $MOUNT_HTTPD_CONFIG_DIR
docker run \
--detach \
--name forward-proxy \
--net ${DOCKER_NETWORK} \
--publish 9092:9092 \
--mount "type=bind,source=${MOUNT_HTTPD_CONFIG_DIR},target=/usr/local/apache2/conf" \
${HTTPD_DOCKER_IMAGE}
#wait_for_url $OAUTH_PROVIDER_URL ${FORWARD_PROXY_URL}
wait_for_message forward-proxy "initializing worker proxy:forward local"
end "forward-proxy is ready"
}

View File

@ -12,7 +12,6 @@ ensure_keycloak() {
init_keycloak() {
KEYCLOAK_CONFIG_PATH=${KEYCLOAK_CONFIG_PATH:-oauth/keycloak}
KEYCLOAK_CONFIG_DIR=$(realpath ${TEST_DIR}/${KEYCLOAK_CONFIG_PATH})
KEYCLOAK_URL=${OAUTH_PROVIDER_URL}
print "> KEYCLOAK_CONFIG_DIR: ${KEYCLOAK_CONFIG_DIR}"
print "> KEYCLOAK_URL: ${KEYCLOAK_URL}"
@ -42,8 +41,9 @@ start_keycloak() {
--publish 8443:8443 \
--env KEYCLOAK_ADMIN=admin \
--env KEYCLOAK_ADMIN_PASSWORD=admin \
--env QUARKUS_HTTP_ACCESS_LOG_ENABLED=true \
-v ${MOUNT_KEYCLOAK_CONF_DIR}:/opt/keycloak/data/import/ \
${KEYCLOAK_DOCKER_IMAGE} start-dev --import-realm \
${KEYCLOAK_DOCKER_IMAGE} start-dev --import-realm --log-level=INFO \
--https-certificate-file=/opt/keycloak/data/import/server_keycloak_certificate.pem \
--https-certificate-key-file=/opt/keycloak/data/import/server_keycloak_key.pem

View File

@ -0,0 +1,45 @@
HTTPD_DOCKER_IMAGE=httpd:latest
ensure_prodkeycloak-proxy() {
if docker ps | grep prodkeycloak-proxy &> /dev/null; then
print "prodkeycloak-proxy already running ..."
else
start_prodkeycloak-proxy
fi
}
init_prodkeycloak-proxy() {
HTTPD_CONFIG_DIR=${TEST_CONFIG_DIR}/prodkeycloak-proxy
PROXY_HOSTNAME=prodkeycloak-proxy
PROXY_PORT=9091
print "> HTTPD_CONFIG: ${HTTPD_CONFIG_DIR}"
print "> PROXY_HOSTNAME: ${PROXY_HOSTNAME}"
print "> PROXY_PORT: ${PROXY_PORT}"
}
start_prodkeycloak-proxy() {
begin "Starting prodkeycloak-proxy ..."
init_prodkeycloak-proxy
kill_container_if_exist prodkeycloak-proxy
MOUNT_HTTPD_CONFIG_DIR=$CONF_DIR/httpd
mkdir -p $MOUNT_HTTPD_CONFIG_DIR
${BIN_DIR}/gen-httpd-conf ${HTTPD_CONFIG_DIR} $ENV_FILE $MOUNT_HTTPD_CONFIG_DIR/httpd.conf
print "> EFFECTIVE HTTPD_CONFIG_FILE: $MOUNT_HTTPD_CONFIG_DIR/httpd.conf"
cp ${HTTPD_CONFIG_DIR}/.htpasswd $MOUNT_HTTPD_CONFIG_DIR
docker run \
--detach \
--name prodkeycloak-proxy \
--net ${DOCKER_NETWORK} \
--publish 9091:9091 \
--mount "type=bind,source=${MOUNT_HTTPD_CONFIG_DIR},target=/usr/local/apache2/conf" \
${HTTPD_DOCKER_IMAGE}
wait_for_message prodkeycloak-proxy "initializing worker proxy:forward local"
end "prodkeycloak-proxy is ready"
}

View File

@ -214,20 +214,37 @@ wait_for_oidc_endpoint_docker() {
calculate_rabbitmq_url() {
echo "${RABBITMQ_SCHEME:-http}://$1${PUBLIC_RABBITMQ_PATH:-$RABBITMQ_PATH}"
}
calculate_forward_proxy_url() {
PROXIED_URL=$1
PROXY_HOSTNAME=$2
PROXY_PORT=$3
SCHEME=$(echo "$PROXIED_URL" | cut -d: -f1)
PATH=$(echo "$PROXIED_URL" | cut -d/ -f4-)
echo "$SCHEME://$PROXY_HOSTNAME:$PROXY_PORT/$PATH"
}
wait_for_url() {
BASE_URL=$1
if [[ $BASE_URL == *"localhost"** ]]; then
wait_for_url_local $BASE_URL
wait_for_url_local $@
else
wait_for_url_docker $BASE_URL
wait_for_url_docker $@
fi
}
wait_for_url_local() {
url=$1
proxy=${2:-none}
proxy_user=${3:-none}
proxy_pass=$4
curl_args="-L -f -v"
max_retry=10
counter=0
until (curl -L -f -v $url >/dev/null 2>&1)
if [[ "$proxy" != "none" && "$proxy" != "" ]]; then
curl_args="--proxy ${proxy} ${curl_args}"
fi
if [[ "$proxy_user" != "none" && "$proxy_user" != "" ]]; then
curl_args="--proxy-user ${proxy_user}:${proxy_pass} ${curl_args}"
fi
until (curl $curl_args $url >/dev/null 2>&1)
do
print "Waiting for $url to start (local)"
sleep 5
@ -240,7 +257,14 @@ wait_for_url_docker() {
url=$1
max_retry=10
counter=0
until (docker run --net ${DOCKER_NETWORK} --rm curlimages/curl:7.85.0 -L -f -v $url >/dev/null 2>&1)
curl_args="-L -f -v"
if [[ "$proxy" != "none" && "$proxy" != "" ]]; then
curl_args="--proxy ${proxy} ${curl_args}"
fi
if [[ "$proxy_user" != "none" && "$proxy_user" != "" ]]; then
curl_args="--proxy-user ${proxy_user}:${proxy_pass} ${curl_args}"
fi
until (docker run --net ${DOCKER_NETWORK} --rm curlimages/curl:7.85.0 $curl_args $url >/dev/null 2>&1)
do
print "Waiting for $url to start (docker)"
sleep 5
@ -373,7 +397,8 @@ profiles_with_local_or_docker() {
generate_env_file() {
begin "Generating env file ..."
mkdir -p $CONF_DIR
${BIN_DIR}/gen-env-file $TEST_CONFIG_DIR $ENV_FILE
${BIN_DIR}/gen-env-file $TEST_CONFIG_DIR ${ENV_FILE}.tmp
grep -v '^#' ${ENV_FILE}.tmp > $ENV_FILE
source $ENV_FILE
end "Finished generating env file."
}
@ -541,7 +566,7 @@ run_on_docker_with() {
build_mocha_image
start_selenium
trap teardown_components EXIT
trap "teardown_components" EXIT
start_components
test
@ -622,7 +647,6 @@ start_components() {
$start
done
}
teardown_components() {
skip_rabbitmq=${1:-false}

View File

@ -5,12 +5,14 @@ authnz-mgt/multi-oauth-with-basic-auth-when-idps-down.sh
authnz-mgt/multi-oauth-with-basic-auth.sh
authnz-mgt/multi-oauth-without-basic-auth-and-resource-label-and-scopes.sh
authnz-mgt/multi-oauth-without-basic-auth.sh
authnz-mgt/multi-oauth-via-proxies.sh
authnz-mgt/oauth-and-basic-auth.sh
authnz-mgt/oauth-idp-initiated-with-uaa-and-prefix-via-proxy.sh
authnz-mgt/oauth-idp-initiated-with-uaa-and-prefix.sh
authnz-mgt/oauth-idp-initiated-with-uaa-via-proxy.sh
authnz-mgt/oauth-idp-initiated-with-uaa.sh
authnz-mgt/oauth-with-keycloak.sh
authnz-mgt/oauth-with-keycloak-via-forward-proxy.sh
authnz-mgt/oauth-with-keycloak-with-verify-none.sh
authnz-mgt/oauth-with-uaa-down-but-with-basic-auth.sh
authnz-mgt/oauth-with-uaa-down.sh

View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
TEST_CASES_PATH=/multi-oauth/without-basic-auth
TEST_CONFIG_PATH=/multi-oauth
PROFILES="devkeycloak prodkeycloak with-resource-label with-resource-scopes tls devkeycloak-proxy prodkeycloak-proxy"
source $SCRIPT/../../bin/suite_template $@
runWith devkeycloak prodkeycloak devkeycloak-proxy prodkeycloak-proxy

View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
TEST_CASES_PATH=/oauth/with-sp-initiated
TEST_CONFIG_PATH=/oauth
PROFILES="forward-proxy keycloak forward-proxy-oauth-provider keycloak-mgt-oauth-provider tls"
source $SCRIPT/../../bin/suite_template $@
runWith keycloak forward-proxy

View File

@ -3,16 +3,12 @@ const { tokenFor, openIdConfiguration } = require('../utils')
const { reset, expectUser, expectVhost, expectResource, allow, verifyAll } = require('../mock_http_backend')
const { open: openAmqp, once: onceAmqp, on: onAmqp, close: closeAmqp } = require('../amqp')
var receivedAmqpMessageCount = 0
var untilConnectionEstablished = new Promise((resolve, reject) => {
onAmqp('connection_open', function(context) {
resolve()
})
})
onAmqp('message', function (context) {
receivedAmqpMessageCount++
})
onceAmqp('sendable', function (context) {
context.sender.send({body:'first message'})
})
@ -52,16 +48,21 @@ describe('Having AMQP 1.0 protocol enabled and the following auth_backends: ' +
})
it('can open an AMQP 1.0 connection', async function () {
var untilFirstMessageReceived = new Promise((resolve, reject) => {
onAmqp('message', function(context) {
resolve()
})
})
amqp = openAmqp()
await untilConnectionEstablished
var untilMessageReceived = new Promise((resolve, reject) => {
await untilFirstMessageReceived
var untilSecondMessageReceived = new Promise((resolve, reject) => {
onAmqp('message', function(context) {
resolve()
})
})
amqp.sender.send({body:'second message'})
await untilMessageReceived
assert.equal(2, receivedAmqpMessageCount)
await untilSecondMessageReceived
})
after(function () {

View File

@ -27,35 +27,32 @@ describe('management user with vhosts permissions', function () {
it('can access overview tab', async function () {
await overview.clickOnOverviewTab()
await overview.waitForOverviewTab()
assert.ok(!await overview.isPopupWarningDisplayed())
assert.ok(await overview.isPopupWarningNotDisplayed())
})
it('can access connections tab', async function () {
await overview.clickOnConnectionsTab()
await overview.waitForConnectionsTab()
assert.ok(!await overview.isPopupWarningDisplayed())
assert.ok(await overview.isPopupWarningNotDisplayed())
})
it('can access channels tab', async function () {
await overview.clickOnChannelsTab()
await overview.waitForChannelsTab()
assert.ok(!await overview.isPopupWarningDisplayed())
assert.ok(await overview.isPopupWarningNotDisplayed())
})
it('can access exchanges tab', async function () {
await overview.clickOnExchangesTab()
await overview.waitForExchangesTab()
assert.ok(!await overview.isPopupWarningDisplayed())
assert.ok(await overview.isPopupWarningNotDisplayed())
})
it('can access queues and streams tab', async function () {
await overview.clickOnQueuesTab()
await overview.waitForQueuesTab()
assert.ok(!await overview.isPopupWarningDisplayed())
assert.ok(await overview.isPopupWarningNotDisplayed())
})
it('can access limited options in admin tab', async function () {
console.log("before clickOnAdminTab")
await overview.clickOnAdminTab()
console.log("before waitForAdminTab")
await overview.waitForAdminTab()
console.log("after waitForAdminTab")
assert.ok(!await overview.isPopupWarningDisplayed())
assert.ok(await overview.isPopupWarningNotDisplayed())
})
it('cannot add/update user limits', async function () {

View File

@ -19,7 +19,6 @@ describe('An user without management tag', function () {
overview = new OverviewPage(driver)
captureScreen = captureScreensFor(driver, __filename)
//assert.ok(!await login.isPopupWarningDisplayed())
await login.login('rabbit_no_management', 'guest')
})
@ -45,8 +44,8 @@ describe('An user without management tag', function () {
it('should close popup warning', async function(){
await delay(1000)
const visible = await login.isPopupWarningDisplayed()
assert.ok(!visible)
const visible =
assert.ok(await login.isPopupWarningNotDisplayed())
})
})

View File

@ -0,0 +1 @@
guest:{SHA}NWdeaPS1r3uZXZIFrQ/EOELxZFA=

View File

@ -0,0 +1,147 @@
#
# This is the main Apache HTTP server configuration file. It contains the
# configuration directives that give the server its instructions.
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
# In particular, see
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
# for a discussion of each configuration directive.
#
# Do NOT simply read the instructions in here without understanding
# what they do. They're here only as hints or reminders. If you are unsure
# consult the online docs. You have been warned.
#
# Configuration and logfile names: If the filenames you specify for many
# of the server's control files begin with "/" (or "drive:/" for Win32), the
# server will use that explicit path. If the filenames do *not* begin
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
# will be interpreted as '/logs/access_log'.
#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# Do not add a slash at the end of the directory path. If you point
# ServerRoot at a non-local disk, be sure to specify a local disk on the
# Mutex directive, if file-based mutexes are used. If you wish to share the
# same ServerRoot for multiple httpd daemons, you will need to change at
# least PidFile.
#
ServerRoot "/usr/local/apache2"
#
# Mutex: Allows you to set the mutex mechanism and mutex file directory
# for individual mutexes, or change the global defaults
#
# Uncomment and change the directory if mutexes are file-based and the default
# mutex file directory is not on a local disk or is not appropriate for some
# other reason.
#
# Mutex default:logs
#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
#Listen 12.34.56.78:80
Listen 9092
#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule unixd_module modules/mod_unixd.so
<IfModule unixd_module>
User www-data
Group www-data
</IfModule>
ServerAdmin you@example.com
ServerName devkeycloak-proxy
ErrorLog /proc/self/fd/2
LogLevel warn
<IfModule log_config_module>
#
# The following directives define some format nicknames for use with
# a CustomLog directive (see below).
#
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
# You need to enable mod_logio.c to use %I and %O
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
#
# The location and format of the access logfile (Common Logfile Format).
# If you do not define any access logfiles within a <VirtualHost>
# container, they will be logged here. Contrariwise, if you *do*
# define per-<VirtualHost> access logfiles, transactions will be
# logged therein and *not* in this file.
#
CustomLog logs/access_log common
#
# If you prefer a logfile with access, agent, and referer information
# (Combined Logfile Format) you can use the following directive.
#
#CustomLog "logs/access_log" combined
</IfModule>
<IfModule proxy_module>
ProxyRequests On
ProxyVia On
<Proxy *>
Allow from all
</Proxy>
</IfModule>
<VirtualHost *:9092>
AllowCONNECT 8442
ProxyRequests On
ProxyVia On
LogLevel debug
ErrorLog /dev/stderr
CustomLog /dev/stdout combined
<Proxy "*">
Allow from all
</Proxy>
</VirtualHost>

View File

@ -0,0 +1,2 @@
export DEVKEYCLOAK_PROXY_HOST=devkeycloak-proxy
export DEVKEYCLOAK_PROXY_PORT=9092

View File

@ -0,0 +1,4 @@
export PRODKEYCLOAK_PROXY_HOST=prodkeycloak-proxy
export PRODKEYCLOAK_PROXY_PORT=9091
export PRODKEYCLOAK_PROXY_USERNAME=guest
export PRODKEYCLOAK_PROXY_PASSWORD=guest

View File

@ -0,0 +1,2 @@
export DEVKEYCLOAK_PROXY_HOST=devkeycloak-proxy
export DEVKEYCLOAK_PROXY_PORT=9092

View File

@ -0,0 +1,4 @@
export PRODKEYCLOAK_PROXY_HOST=prodkeycloak-proxy
export PRODKEYCLOAK_PROXY_PORT=9091
export PRODKEYCLOAK_PROXY_USERNAME=guest
export PRODKEYCLOAK_PROXY_PASSWORD=guest

View File

@ -0,0 +1 @@
guest:{SHA}NWdeaPS1r3uZXZIFrQ/EOELxZFA=

View File

@ -0,0 +1,151 @@
#
# This is the main Apache HTTP server configuration file. It contains the
# configuration directives that give the server its instructions.
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
# In particular, see
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
# for a discussion of each configuration directive.
#
# Do NOT simply read the instructions in here without understanding
# what they do. They're here only as hints or reminders. If you are unsure
# consult the online docs. You have been warned.
#
# Configuration and logfile names: If the filenames you specify for many
# of the server's control files begin with "/" (or "drive:/" for Win32), the
# server will use that explicit path. If the filenames do *not* begin
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
# will be interpreted as '/logs/access_log'.
#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# Do not add a slash at the end of the directory path. If you point
# ServerRoot at a non-local disk, be sure to specify a local disk on the
# Mutex directive, if file-based mutexes are used. If you wish to share the
# same ServerRoot for multiple httpd daemons, you will need to change at
# least PidFile.
#
ServerRoot "/usr/local/apache2"
#
# Mutex: Allows you to set the mutex mechanism and mutex file directory
# for individual mutexes, or change the global defaults
#
# Uncomment and change the directory if mutexes are file-based and the default
# mutex file directory is not on a local disk or is not appropriate for some
# other reason.
#
# Mutex default:logs
#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
#Listen 12.34.56.78:80
Listen 9091
#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule unixd_module modules/mod_unixd.so
<IfModule unixd_module>
User www-data
Group www-data
</IfModule>
ServerAdmin you@example.com
ServerName prodkeycloak-proxy
ErrorLog /proc/self/fd/2
LogLevel warn
<IfModule log_config_module>
#
# The following directives define some format nicknames for use with
# a CustomLog directive (see below).
#
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
# You need to enable mod_logio.c to use %I and %O
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
#
# The location and format of the access logfile (Common Logfile Format).
# If you do not define any access logfiles within a <VirtualHost>
# container, they will be logged here. Contrariwise, if you *do*
# define per-<VirtualHost> access logfiles, transactions will be
# logged therein and *not* in this file.
#
CustomLog logs/access_log common
#
# If you prefer a logfile with access, agent, and referer information
# (Combined Logfile Format) you can use the following directive.
#
#CustomLog "logs/access_log" combined
</IfModule>
<IfModule proxy_module>
ProxyRequests On
ProxyVia On
<Proxy *>
Allow from all
</Proxy>
</IfModule>
<VirtualHost *:9091>
AllowCONNECT 8443
ProxyRequests On
ProxyVia On
LogLevel debug
ErrorLog /dev/stderr
CustomLog /dev/stdout combined
<Proxy "*">
Allow from all
AuthType Basic
AuthName "Restricted Site"
AuthBasicProvider file
AuthUserFile /usr/local/apache2/conf/.htpasswd
Require valid-user
</Proxy>
</VirtualHost>

View File

@ -0,0 +1,2 @@
auth_oauth2.oauth_providers.devkeycloak.proxy.host = ${DEVKEYCLOAK_PROXY_HOST}
auth_oauth2.oauth_providers.devkeycloak.proxy.port = ${DEVKEYCLOAK_PROXY_PORT}

View File

@ -0,0 +1,4 @@
auth_oauth2.oauth_providers.prodkeycloak.proxy.host = ${PRODKEYCLOAK_PROXY_HOST}
auth_oauth2.oauth_providers.prodkeycloak.proxy.port = ${PRODKEYCLOAK_PROXY_PORT}
auth_oauth2.oauth_providers.prodkeycloak.proxy.username = ${PRODKEYCLOAK_PROXY_USERNAME}
auth_oauth2.oauth_providers.prodkeycloak.proxy.password = ${PRODKEYCLOAK_PROXY_PASSWORD}

View File

@ -33,9 +33,7 @@ describe('When basic authentication is enabled but both Idps are down', function
it('should not be presented oauth2 section', async function () {
await homePage.isLoaded()
if (await homePage.isOAuth2SectionVisible()) {
throw new Error('OAuth2 section should not be present')
}
assert.ok(await homePage.isOAuth2SectionNotVisible())
})
after(async function () {

View File

@ -45,8 +45,7 @@ describe('Given two oauth resources and basic auth enabled, an unauthenticated u
})
it('should not have a warning message', async function () {
const visible = await homePage.isWarningVisible()
assert.ok(!visible)
assert.ok(await homePage.isWarningNotVisible())
})

View File

@ -32,7 +32,7 @@ describe('Given there are three oauth resources but two enabled', function () {
if (!await overview.isLoaded()) {
throw new Error('Failed to login')
}
assert.ok(!await overview.isPopupWarningDisplayed())
assert.ok(await overview.isPopupWarningNotDisplayed())
await overview.logout()
})
it('prod_user registered in prodkeycloak can log in using RabbitMQ Development OAuth 2.0 resource', async function () {
@ -46,7 +46,7 @@ describe('Given there are three oauth resources but two enabled', function () {
if (!await overview.isLoaded()) {
throw new Error('Failed to login')
}
assert.ok(!await overview.isPopupWarningDisplayed())
assert.ok(await overview.isPopupWarningNotDisplayed())
await overview.logout()
})

View File

@ -39,11 +39,11 @@ describe('Given three oauth resources but only two enabled, an unauthenticated u
})
it('should not be presented with a login button to log in using Basic Auth', async function () {
assert.ok(!await homePage.isBasicAuthSectionVisible())
assert.ok(await homePage.isBasicAuthSectionNotVisible())
})
it('should not have a warning message', async function () {
assert.ok(!await homePage.isWarningVisible())
assert.ok(await homePage.isWarningNotVisible())
})

View File

@ -0,0 +1,6 @@
export FORWARD_PROXY_HOST=forward-proxy
export FORWARD_PROXY_PORT=9092
export FORWARD_PROXY_USERNAME=guest
export FORWARD_PROXY_PASSWORD=guest
export OAUTH_PROVIDER_URL=${KEYCLOAK_URL}
export OAUTH_PROVIDER_CA_CERT=${KEYCLOAK_CA_CERT}

View File

@ -1,3 +1,2 @@
export KEYCLOAK_URL=https://keycloak:8443/realms/test
export OAUTH_PROVIDER_URL=https://keycloak:8443/realms/test
export OAUTH_PROVIDER_CA_CERT=/config/oauth/keycloak/ca_keycloak_certificate.pem
export KEYCLOAK_CA_CERT=/config/oauth/keycloak/ca_keycloak_certificate.pem

View File

@ -1 +1,2 @@
# export OAUTH_PROVIDER_URL=${KEYCLOAK_URL}
export OAUTH_PROVIDER_URL=${KEYCLOAK_URL}
export OAUTH_PROVIDER_CA_CERT=${OAUTH_SERVER_CONFIG_DIR}/ca_keycloak_certificate.pem

View File

@ -0,0 +1,6 @@
export FORWARD_PROXY_HOST=forward-proxy
export FORWARD_PROXY_PORT=9092
export FORWARD_PROXY_USERNAME=guest
export FORWARD_PROXY_PASSWORD=guest
export OAUTH_PROVIDER_URL=${KEYCLOAK_URL}
export OAUTH_PROVIDER_CA_CERT=${KEYCLOAK_CA_CERT}

View File

@ -1,3 +1,2 @@
export KEYCLOAK_URL=https://localhost:8443/realms/test
export OAUTH_PROVIDER_URL=https://localhost:8443/realms/test
export OAUTH_PROVIDER_CA_CERT=selenium/test/oauth/keycloak/ca_keycloak_certificate.pem
export KEYCLOAK_URL=https://keycloak:8443/realms/test
export KEYCLOAK_CA_CERT=selenium/test/oauth/keycloak/ca_keycloak_certificate.pem

View File

@ -0,0 +1 @@
guest:{SHA}NWdeaPS1r3uZXZIFrQ/EOELxZFA=

View File

@ -0,0 +1,165 @@
#
# This is the main Apache HTTP server configuration file. It contains the
# configuration directives that give the server its instructions.
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
# In particular, see
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
# for a discussion of each configuration directive.
#
# Do NOT simply read the instructions in here without understanding
# what they do. They're here only as hints or reminders. If you are unsure
# consult the online docs. You have been warned.
#
# Configuration and logfile names: If the filenames you specify for many
# of the server's control files begin with "/" (or "drive:/" for Win32), the
# server will use that explicit path. If the filenames do *not* begin
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
# will be interpreted as '/logs/access_log'.
#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# Do not add a slash at the end of the directory path. If you point
# ServerRoot at a non-local disk, be sure to specify a local disk on the
# Mutex directive, if file-based mutexes are used. If you wish to share the
# same ServerRoot for multiple httpd daemons, you will need to change at
# least PidFile.
#
ServerRoot "/usr/local/apache2"
#
# Mutex: Allows you to set the mutex mechanism and mutex file directory
# for individual mutexes, or change the global defaults
#
# Uncomment and change the directory if mutexes are file-based and the default
# mutex file directory is not on a local disk or is not appropriate for some
# other reason.
#
# Mutex default:logs
#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
#Listen 12.34.56.78:80
Listen 9092
#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule unixd_module modules/mod_unixd.so
<IfModule unixd_module>
User www-data
Group www-data
</IfModule>
ServerAdmin you@example.com
ServerName forward-proxy
ErrorLog /proc/self/fd/2
LogLevel warn
<IfModule log_config_module>
#
# The following directives define some format nicknames for use with
# a CustomLog directive (see below).
#
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
# You need to enable mod_logio.c to use %I and %O
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
#
# The location and format of the access logfile (Common Logfile Format).
# If you do not define any access logfiles within a <VirtualHost>
# container, they will be logged here. Contrariwise, if you *do*
# define per-<VirtualHost> access logfiles, transactions will be
# logged therein and *not* in this file.
#
CustomLog logs/access_log common
#
# If you prefer a logfile with access, agent, and referer information
# (Combined Logfile Format) you can use the following directive.
#
#CustomLog "logs/access_log" combined
</IfModule>
<IfModule proxy_module>
ProxyRequests On
ProxyVia On
<Proxy *>
Allow from all
</Proxy>
</IfModule>
<VirtualHost *:9092>
# SSLEngine on
# SSLCertificateKeyFile /usr/local/apache2/conf/server_forward-proxy_key.pem
# SSLCertificateFile /usr/local/apache2/conf/server_forward-proxy_certificate.pem
# SSLCertificateChainFile /usr/local/apache2/conf/ca_keycloak_certificate.pem
# SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
# SSLProxyVerify none
AllowCONNECT 8443
# SSLProxyEngine On
# SSLProxyVerify none
# SSLProxyCheckPeerCN off
# SSLProxyCheckPeerName off
# SSLProxyCheckPeerExpire off
# SSLProxyProtocol +TLSv1.2
ProxyRequests On
ProxyVia On
LogLevel debug
ErrorLog /dev/stderr
CustomLog /dev/stdout combined
<Proxy "*">
Allow from all
AuthType Basic
AuthName "Restricted Site"
AuthBasicProvider file
AuthUserFile /usr/local/apache2/conf/.htpasswd
Require valid-user
</Proxy>
</VirtualHost>

View File

@ -56,6 +56,9 @@
"vhosts": [
{
"name": "/"
},
{
"name": "other"
}
],
"permissions": [

View File

@ -0,0 +1,6 @@
auth_oauth2.issuer = ${OAUTH_PROVIDER_URL}
auth_oauth2.https.cacertfile = ${KEYCLOAK_CA_CERT}
auth_oauth2.proxy.host = ${FORWARD_PROXY_HOST}
auth_oauth2.proxy.port = ${FORWARD_PROXY_PORT}
auth_oauth2.proxy.username = ${FORWARD_PROXY_USERNAME}
auth_oauth2.proxy.password = ${FORWARD_PROXY_PASSWORD}

View File

@ -1,3 +1,3 @@
# uaa requires a secret in order to renew tokens
management.oauth_provider_url = ${KEYCLOAK_URL}
#management.oauth_provider_url = ${KEYCLOAK_URL}
management.oauth_authorization_endpoint_params.resource = rabbitmq

View File

@ -1,2 +1,2 @@
auth_oauth2.issuer = ${OAUTH_PROVIDER_URL}
auth_oauth2.https.cacertfile = ${OAUTH_PROVIDER_CA_CERT}
auth_oauth2.issuer = ${KEYCLOAK_URL}
auth_oauth2.https.cacertfile = ${KEYCLOAK_CA_CERT}

View File

@ -1,2 +1,2 @@
auth_oauth2.issuer = ${OAUTH_PROVIDER_URL}
auth_oauth2.issuer = ${KEYCLOAK_URL}
auth_oauth2.https.peer_verification = verify_none

View File

@ -1,2 +1,4 @@
# uaa requires a secret in order to renew tokens
management.oauth_provider_url = ${UAA_URL}
# uaa requires a secret in order to renew tokens
management.oauth_client_secret = ${OAUTH_CLIENT_SECRET}

View File

@ -1,5 +1,3 @@
# uaa requires a secret in order to renew tokens
management.oauth_client_secret = ${OAUTH_CLIENT_SECRET}
# configure static signing keys and the oauth provider used by the plugin
auth_oauth2.default_key = ${OAUTH_SIGNING_KEY_ID}

View File

@ -21,7 +21,7 @@ describe('When basic authentication is enabled but UAA is down', function () {
it('can log in with Basic Auth', async function () {
await homePage.toggleBasicAuthSection()
assert.ok(await homePage.isLoginButtonVisible)
assert.ok(await homePage.getBasicAuthLoginButton())
await homePage.basicAuthLogin('guest', 'guest')
await overview.isLoaded()
assert.equal(await overview.getUser(), 'User guest')

View File

@ -20,15 +20,13 @@ describe('When basic authentication is enabled but UAA is down', function () {
it('should display warning message that UAA is down', async function () {
await homePage.isLoaded()
const message = await homePage.getWarning()
assert.equal(true, message.startsWith('OAuth resource [rabbitmq] not available'))
assert.equal(true, message.endsWith(' not reachable'))
assert.ok(message.startsWith('OAuth resource [rabbitmq] not available'))
assert.ok(message.endsWith(' not reachable'))
})
it('should not be presented oauth2 section', async function () {
await homePage.isLoaded()
if (await homePage.isOAuth2SectionVisible()) {
throw new Error('OAuth2 section should not be present')
}
assert.ok(await homePage.isOAuth2SectionNotVisible())
})
after(async function () {

View File

@ -30,8 +30,7 @@ describe('A user which accesses any protected URL without a session where basic
})
it('should not have a warning message', async function () {
const visible = await homePage.isWarningVisible()
assert.ok(!visible)
assert.ok(await homePage.isWarningNotVisible())
})

View File

@ -28,15 +28,14 @@ describe('An user without management tag', function () {
})
it('cannot log in into the management ui', async function () {
const visible = await homePage.isWarningVisible()
assert.ok(visible)
assert.ok(await homePage.isWarningVisible())
})
it('should get "Not authorized" warning message', async function(){
assert.equal('Not authorized', await homePage.getWarning())
assert.equal('Click here to logout', await homePage.getLogoutButton())
assert.ok(!await homePage.isBasicAuthSectionVisible())
assert.ok(!await homePage.isOAuth2SectionVisible())
assert.ok(await homePage.isBasicAuthSectionNotVisible())
assert.ok(await homePage.isOAuth2SectionNotVisible())
})
describe("After clicking on logout button", function() {
@ -46,8 +45,7 @@ describe('An user without management tag', function () {
})
it('should get redirected to home page again without error message', async function(){
const visible = await homePage.isWarningVisible()
assert.ok(!visible)
assert.ok(await homePage.isWarningNotVisible())
})
})

View File

@ -20,13 +20,13 @@ describe('When UAA is down', function () {
it('should display warning message that UAA is down', async function () {
await homePage.isLoaded()
const message = await homePage.getWarning()
assert.equal(true, message.startsWith('OAuth resource [rabbitmq] not available'))
assert.equal(true, message.endsWith(' not reachable'))
assert.ok(message.startsWith('OAuth resource [rabbitmq] not available'))
assert.ok(message.endsWith(' not reachable'))
})
it('should not be presented with a login button to log in', async function () {
await homePage.isLoaded()
assert.equal(false, await homePage.isLoginButtonVisible())
assert.ok(await homePage.isLoginButtonNotVisible())
})
after(async function () {

View File

@ -1,15 +1,13 @@
const { By, Key, until, Builder } = require('selenium-webdriver')
require('chromedriver')
const assert = require('assert')
const { buildDriver, goToLogin, goToHome, captureScreensFor, teardown } = require('../../utils')
const { buildDriver, goToHome, captureScreensFor, teardown } = require('../../utils')
const OverviewPage = require('../../pageobjects/OverviewPage')
describe('A user with a JWT token', function () {
let overview
let captureScreen
let token
let fakePortal
before(async function () {
driver = buildDriver()

View File

@ -21,14 +21,12 @@ describe('A user which accesses any protected URL without a session', function (
it('should be presented with a login button to log in', async function () {
await homePage.isLoaded()
const value = await homePage.getLoginButton()
assert.equal(value, 'Click here to log in')
assert.equal(await homePage.getLoginButton(), 'Click here to log in')
})
it('should not have a warning message', async function () {
await homePage.isLoaded()
const visible = await homePage.isWarningVisible()
assert.ok(!visible)
assert.ok(await homePage.isWarningNotVisible())
})
it('login button should redirect to the configured oauth_provider_url', async function () {

View File

@ -36,8 +36,7 @@ describe('Once user logs in with its own token', function () {
it('user should be presented with a login button to log in', async function () {
await homePage.isLoaded()
const value = await homePage.getLoginButton()
assert.equal(value, 'Click here to log in')
assert.equal(await homePage.getLoginButton(), 'Click here to log in')
})
after(async function () {

View File

@ -24,12 +24,10 @@ describe('A user who accesses the /login URL with a token without scopes for the
})
it('should get a warning message', async function () {
const message = await homePage.getWarning()
assert.equal('Not_Authorized', message)
assert.equal(await homePage.getWarning(), 'Not_Authorized')
})
it('should be presented with a login button to log in', async function () {
const value = await homePage.getLoginButton()
assert.equal(value, 'Click here to log in')
assert.equal(await homePage.getLoginButton(), 'Click here to log in')
})
after(async function () {

View File

@ -29,14 +29,12 @@ describe('A user which accesses any protected URL without a session', function (
assert.equal("rabbit_x", resources[2].value)
assert.equal("RabbitMQ X_Idp", resources[2].text)
const value = await homePage.getLoginButton()
assert.equal(value, 'Click here to log in')
assert.equal(await homePage.getLoginButton(), 'Click here to log in')
})
it('should not have a warning message', async function () {
await homePage.isLoaded()
const visible = await homePage.isWarningVisible()
assert.ok(!visible)
assert.ok(await homePage.isWarningNotVisible())
})
after(async function () {

View File

@ -18,14 +18,12 @@ describe('A user which accesses any protected URL without a session', function (
it('should be presented with a login button to log in', async function () {
await homePage.isLoaded()
const value = await homePage.getLoginButton()
assert.equal(value, 'Click here to log in')
assert.equal(await homePage.getLoginButton(), 'Click here to log in')
})
it('should not have a warning message', async function () {
await homePage.isLoaded()
const visible = await homePage.isWarningVisible()
assert.ok(!visible)
assert.ok(await homePage.isWarningNotVisible())
})
after(async function () {

View File

@ -26,14 +26,11 @@ describe('A user which accesses a protected URL without a session', function ()
it('redirect to previous accessed page after login ', async function () {
await homePage.clickToLogin()
await idpLogin.login('rabbit_admin', 'rabbit_admin')
if (!await exchanges.isLoaded()) {
throw new Error('Failed to login')
}
assert.equal("All exchanges (8)", await exchanges.getPagingSectionHeaderText())
await exchanges.getPagingSectionHeaderText()
})

View File

@ -29,15 +29,14 @@ describe('An user without management tag', function () {
if (!await homePage.isLoaded()) {
throw new Error('Failed to login')
}
const visible = await homePage.isWarningVisible()
assert.ok(visible)
assert.ok(await homePage.isWarningVisible())
})
it('should get "Not authorized" warning message and logout button but no login button', async function(){
assert.equal('Not authorized', await homePage.getWarning())
assert.equal('Click here to logout', await homePage.getLogoutButton())
assert.ok(!await homePage.isBasicAuthSectionVisible())
assert.ok(!await homePage.isOAuth2SectionVisible())
assert.ok(await homePage.isBasicAuthSectionNotVisible())
assert.ok(await homePage.isOAuth2SectionNotVisible())
})
describe("After clicking on logout button", function() {
@ -48,8 +47,7 @@ describe('An user without management tag', function () {
it('should get redirected to home page again without error message', async function(){
await driver.sleep(250)
const visible = await homePage.isWarningVisible()
assert.ok(!visible)
assert.ok(await homePage.isWarningNotVisible())
})
})

View File

@ -147,17 +147,11 @@ module.exports = class BasePage {
} catch(e) {
return Promise.resolve(false)
}
/*
let element = await driver.findElement(FORM_POPUP)
return this.driver.wait(until.elementIsVisible(element), this.timeout / 2,
'Timed out after [timeout=' + this.timeout + ';polling=' + this.polling + '] awaiting till visible ' + element,
this.polling / 2).then(function onWarningVisible(e) {
return Promise.resolve(true)
}, function onError(e) {
return Promise.resolve(false)
})
*/
}
async isPopupWarningNotDisplayed() {
return this.waitForNotDisplayed(FORM_POPUP)
}
async getPopupWarning() {
let element = await driver.findElement(FORM_POPUP)
return this.driver.wait(until.elementIsVisible(element), this.timeout,
@ -208,15 +202,38 @@ module.exports = class BasePage {
async waitForDisplayed (locator) {
if (this.interactionDelay && this.interactionDelay > 0) await this.driver.sleep(this.interactionDelay)
let times = 1
do {
try {
return this.waitForVisible(await this.waitForLocated(locator))
}catch(error) {
if (!error.name.includes("NoSuchSessionError")) {
console.error("Failed to waitForDisplayed " + locator + " due to " + error)
}
if (times > 0 && error.name.includes("StaleElementReference")) {
times--
this.driver.sleep(50)
} else {
throw error
}
}
} while (true)
}
async waitForNotDisplayed (locator) {
let times = 5
do {
try {
await driver.findElement(locator)
times--
this.driver.sleep(50)
}catch(error) {
return Promise.resolve(true)
}
} while(times > 0)
return Promise.resolve(false)
}
async getText (locator) {
const element = await this.waitForDisplayed(locator)

View File

@ -51,6 +51,9 @@ module.exports = class SSOHomePage extends BasePage {
async getOAuthResourceOptions () {
return this.getSelectableOptions(SELECT_RESOURCES)
}
async isLoginButtonNotVisible() {
return this.waitForNotDisplayed(OAUTH2_LOGIN_BUTTON)
}
async isLoginButtonVisible() {
try {
await this.waitForDisplayed(OAUTH2_LOGIN_BUTTON)
@ -71,12 +74,18 @@ module.exports = class SSOHomePage extends BasePage {
async isOAuth2SectionVisible() {
return this.isDisplayed(SECTION_LOGIN_WITH_OAUTH)
}
async isOAuth2SectionNotVisible() {
return this.waitForNotDisplayed(SECTION_LOGIN_WITH_OAUTH)
}
async getOAuth2Section() {
return this.waitForDisplayed(SECTION_LOGIN_WITH_OAUTH)
}
async isBasicAuthSectionVisible() {
return this.isDisplayed(SECTION_LOGIN_WITH_BASIC_AUTH)
}
async isBasicAuthSectionNotVisible() {
return this.waitForNotDisplayed(SECTION_LOGIN_WITH_BASIC_AUTH)
}
async getBasicAuthSection() {
return this.waitForDisplayed(SECTION_LOGIN_WITH_BASIC_AUTH)
}
@ -104,6 +113,9 @@ module.exports = class SSOHomePage extends BasePage {
return Promise.resolve(false)
}
}
async isWarningNotVisible () {
return this.waitForNotDisplayed(WARNING)
}
async getWarnings() {
try
{

View File

@ -3,15 +3,17 @@ const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
const fsp = fs.promises
const path = require('path')
const { By, Key, until, Builder, logging, Capabilities } = require('selenium-webdriver')
const proxy = require('selenium-webdriver/proxy')
require('chromedriver')
const UAALoginPage = require('./pageobjects/UAALoginPage')
const KeycloakLoginPage = require('./pageobjects/KeycloakLoginPage')
const assert = require('assert')
const runLocal = String(process.env.RUN_LOCAL).toLowerCase() != 'false'
const uaaUrl = process.env.UAA_URL || 'http://localhost:8080'
const baseUrl = randomly_pick_baseurl(process.env.RABBITMQ_URL) || 'http://localhost:15672/'
//const proxyUrl = calculateProxyUrl(baseUrl)
const hostname = process.env.RABBITMQ_HOSTNAME || 'localhost'
const runLocal = String(process.env.RUN_LOCAL).toLowerCase() != 'false'
const seleniumUrl = process.env.SELENIUM_URL || 'http://selenium:4444'
const screenshotsDir = process.env.SCREENSHOTS_DIR || '/screens'
const profiles = process.env.PROFILES || ''
@ -23,6 +25,19 @@ function randomly_pick_baseurl(baseUrl) {
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
/*
function calculateProxyUrl(baseUrl) {
if (baseUrl.includes("localhost")) {
return undefined
}
if (process.env.FORWARD_PROXY_HOST && process.env.FORWARD_PROXY_PORT) {
return 'http://' + process.env.FORWARD_PROXY_HOST + ':' + process.env.FORWARD_PROXY_PORT
}else {
console.log("There are no proxy setup : " + JSON.stringify(process.env))
return undefined
}
}
*/
class CaptureScreenshot {
driver
test
@ -64,10 +79,19 @@ module.exports = {
"--disable-search-engine-choice-screen"
]
});
driver = builder
builder = builder
.forBrowser('chrome')
.withCapabilities(chromeCapabilities)
.build()
/*
if (proxyUrl) {
console.log("Using proxy " + proxyUrl)
builder = builder.setProxy(proxy.manual({https: proxyUrl}))
}else {
console.log("Not Using proxy . runLocal: " + runLocal)
}
*/
driver = builder.build()
driver.manage().setTimeouts( { pageLoad: 35000 } )
return driver
},