Skip to content

Commit 1e50258

Browse files
Added introspect endpoint
so that management can cache the jwt token resulting from introspecting an opaque one
1 parent 345d5af commit 1e50258

File tree

4 files changed

+126
-2
lines changed

4 files changed

+126
-2
lines changed

deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ dispatcher() ->
214214
{"/reset/:node", rabbit_mgmt_wm_reset, []},
215215
{"/rebalance/queues", rabbit_mgmt_wm_rebalance_queues, [{queues, all}]},
216216
{"/auth", rabbit_mgmt_wm_auth, []},
217+
{"/auth/introspect", rabbit_mgmt_wm_oauth_introspect, []},
217218
{"/auth/attempts/:node", rabbit_mgmt_wm_auth_attempts, [all]},
218219
{"/auth/attempts/:node/source", rabbit_mgmt_wm_auth_attempts, [by_source]},
219220
{"/login", rabbit_mgmt_wm_login, []},
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
%% This Source Code Form is subject to the terms of the Mozilla Public
2+
%% License, v. 2.0. If a copy of the MPL was not distributed with this
3+
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
%%
5+
%% Copyright (c) 2007-2025 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
%%
7+
8+
-module(rabbit_mgmt_wm_oauth_introspect).
9+
10+
-export([init/2, to_json/2, content_types_provided/2, is_authorized/2]).
11+
-include("rabbit_mgmt.hrl").
12+
13+
%%--------------------------------------------------------------------
14+
15+
init(Req, State) ->
16+
{cowboy_rest, rabbit_mgmt_headers:set_no_cache_headers(
17+
rabbit_mgmt_headers:set_common_permission_headers(Req, ?MODULE), ?MODULE), State}.
18+
19+
allowed_methods(ReqData, Context) ->
20+
{[<<"POST">>, <<"OPTIONS">>], ReqData, Context}.
21+
22+
variances(Req, Context) ->
23+
{[<<"accept-encoding">>, <<"origin">>], Req, Context}.
24+
25+
content_types_provided(ReqData, Context) ->
26+
{rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
27+
28+
to_json(ReqData, Context) ->
29+
case cowboy_req:parse_header(<<"authorization">>, ReqData) of
30+
{bearer, Token} ->
31+
rabbit_mgmt_util:reply(
32+
maps:from_list(oauth2_client:introspect_token(Token)),
33+
ReqData, Context);
34+
_ ->
35+
rabbit_mgmt_util:bad_request(<<"Opaque token not found in authorization header">>, ReqData, Context)
36+
end.
37+
38+
is_authorized(ReqData, Context) ->
39+
{true, ReqData, Context}.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-module(introspect_http_handler).
2+
-behavior(cowboy_handler).
3+
4+
-export([init/2, terminate/3]).
5+
6+
init(Req, State) ->
7+
case cowboy_req:parse_header(<<"authorization">>, Req) of
8+
{bearer, <<"active">>} ->
9+
Body = rabbit_json:encode([{"active", true}, {"scope", "rabbitmq.tag:administrator"}]),
10+
{ok, cowboy_req:reply(200, #{<<"content-type">> => <<"application/json">>},
11+
Body, Req), State};
12+
{bearer, <<"inactive">>} ->
13+
Body = rabbit_json:encode([{"active", false}, {"scope", "rabbitmq.tag:administrator"}]),
14+
{ok, cowboy_req:reply(200, #{<<"content-type">> => <<"application/json">>},
15+
Body, Req), State};
16+
_ ->
17+
{ok, cowboy_req:reply(401, #{}, [], Req), State}
18+
end.
19+
20+
terminate(_Reason, _Req, _State) ->
21+
ok.

deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
-include_lib("eunit/include/eunit.hrl").
1212
-import(application, [set_env/3, unset_env/2]).
1313
-import(rabbit_mgmt_wm_auth, [authSettings/0]).
14+
-import(rabbit_mgmt_test_util, [req/5]).
1415
-compile(export_all).
1516

1617
all() ->
@@ -27,12 +28,17 @@ all() ->
2728
{group, verify_oauth_initiated_logon_type_for_idp_initiated},
2829
{group, verify_oauth_disable_basic_auth},
2930
{group, verify_oauth_scopes},
30-
{group, verify_extra_endpoint_params}
31+
{group, verify_extra_endpoint_params},
32+
{group, run_with_broker}
3133
].
3234

3335
groups() ->
3436
[
35-
37+
{run_with_broker, [], [
38+
{verify_introspection_endpoint, [], [
39+
introspect_opaque_token_returns_active_jwt_token
40+
]}
41+
]},
3642
{verify_multi_resource_and_provider, [], [
3743
{with_oauth_enabled, [], [
3844
{with_oauth_providers_idp1_idp2, [], [
@@ -510,6 +516,26 @@ init_per_group(with_mgt_resource_server_a_with_token_endpoint_params_1, Config)
510516
?config(a, Config), oauth_token_endpoint_params, ?config(token_params_1, Config)),
511517
Config;
512518

519+
init_per_group(run_with_broker, Config) ->
520+
Config1 = finish_init(run_with_broker, Config),
521+
start_broker(Config1);
522+
523+
init_per_group(verify_introspection_endpoint, Config) ->
524+
{ok, _} = application:ensure_all_started(ssl),
525+
{ok, _} = application:ensure_all_started(cowboy),
526+
527+
PortBase = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_ports_base),
528+
Port = PortBase + 100,
529+
530+
CertsDir = ?config(rmq_certsdir, Config),
531+
Endpoints = [ {"/introspect", introspect_endpoint, []}],
532+
Dispatch = cowboy_router:compile([{'_', Endpoints}]),
533+
{ok, _} = cowboy:start_tls(introspection_http_listener,
534+
[{port, Port},
535+
{certfile, filename:join([CertsDir, "server", "cert.pem"])},
536+
{keyfile, filename:join([CertsDir, "server", "key.pem"])}],
537+
#{env => #{dispatch => Dispatch}}),
538+
Config;
513539

514540
init_per_group(_, Config) ->
515541
Config.
@@ -632,11 +658,44 @@ end_per_group(with_mgt_resource_server_a_with_token_endpoint_params_1, Config) -
632658
?config(a, Config), oauth_token_endpoint_params),
633659
Config;
634660

661+
end_per_group(run_with_broker, Config) ->
662+
Teardown0 = rabbit_ct_client_helpers:teardown_steps(),
663+
Teardown1 = rabbit_ct_broker_helpers:teardown_steps(),
664+
Steps = Teardown0 ++ Teardown1,
665+
rabbit_ct_helpers:run_teardown_steps(Config, Steps),
666+
Config;
667+
668+
end_per_group(verify_introspection_endpoint, Config) ->
669+
ok = cowboy:stop_listener(introspection_http_listener),
670+
inets:stop(),
671+
Config;
635672

636673
end_per_group(_, Config) ->
637674
Config.
638675

639676

677+
start_broker(Config) ->
678+
Setup0 = rabbit_ct_broker_helpers:setup_steps(),
679+
Setup1 = rabbit_ct_client_helpers:setup_steps(),
680+
Steps = Setup0 ++ Setup1,
681+
case rabbit_ct_helpers:run_setup_steps(Config, Steps) of
682+
{skip, _} = Skip ->
683+
Skip;
684+
Config1 ->
685+
Ret = rabbit_ct_broker_helpers:enable_feature_flag(
686+
Config1, 'rabbitmq_4.0.0'),
687+
case Ret of
688+
ok -> Config1;
689+
_ -> Ret
690+
end
691+
end.
692+
finish_init(Group, Config) ->
693+
rabbit_ct_helpers:log_environment(),
694+
inets:start(),
695+
NodeConf = [{rmq_nodename_suffix, Group}],
696+
rabbit_ct_helpers:set_config(Config, NodeConf).
697+
698+
640699
%% -------------------------------------------------------------------
641700
%% Test cases.
642701
%% -------------------------------------------------------------------
@@ -838,6 +897,10 @@ should_return_mgt_oauth_resource_a_with_token_endpoint_params_1(Config) ->
838897
assertEqual_on_attribute_for_oauth_resource_server(authSettings(),
839898
Config, a, oauth_token_endpoint_params, token_params_1).
840899

900+
introspect_opaque_token_returns_active_jwt_token(Config) ->
901+
_Result = req(Config, 0, post, "/introspect", [{"Authorization", "Bearer active"}]).
902+
903+
841904
%% -------------------------------------------------------------------
842905
%% Utility/helper functions
843906
%% -------------------------------------------------------------------

0 commit comments

Comments
 (0)