Skip to content

Commit 6c50ce2

Browse files
Merge pull request #12439 from rabbitmq/support-scope-aliases-in-cuttlefish-style
OAuth 2: support scope aliases in rabbitmq.conf
2 parents b5c252e + 423b591 commit 6c50ce2

File tree

4 files changed

+342
-34
lines changed

4 files changed

+342
-34
lines changed

deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,26 @@
7373
list_to_binary(cuttlefish:conf_get("auth_oauth2.additional_scopes_key", Conf))
7474
end}.
7575

76+
{mapping,
77+
"auth_oauth2.scope_aliases.$alias",
78+
"rabbitmq_auth_backend_oauth2.scope_aliases",
79+
[{datatype, string}]}.
80+
81+
{mapping,
82+
"auth_oauth2.scope_aliases.$index.alias",
83+
"rabbitmq_auth_backend_oauth2.scope_aliases",
84+
[{datatype, string}]}.
85+
86+
{mapping,
87+
"auth_oauth2.scope_aliases.$index.scope",
88+
"rabbitmq_auth_backend_oauth2.scope_aliases",
89+
[{datatype, string}]}.
90+
91+
{translation,
92+
"rabbitmq_auth_backend_oauth2.scope_aliases",
93+
fun(Conf) ->
94+
rabbit_oauth2_schema:translate_scope_aliases(Conf)
95+
end}.
7696

7797
%% Configure the plugin to skip validation of the aud field
7898
%%
@@ -355,6 +375,21 @@
355375
[{datatype, string}]
356376
}.
357377

378+
{mapping,
379+
"auth_oauth2.resource_servers.$name.scope_aliases.$alias",
380+
"rabbitmq_auth_backend_oauth2.resource_servers",
381+
[{datatype, string}]}.
382+
383+
{mapping,
384+
"auth_oauth2.resource_servers.$name.scope_aliases.$index.alias",
385+
"rabbitmq_auth_backend_oauth2.resource_servers",
386+
[{datatype, string}]}.
387+
388+
{mapping,
389+
"auth_oauth2.resource_servers.$name.scope_aliases.$index.scope",
390+
"rabbitmq_auth_backend_oauth2.resource_servers",
391+
[{datatype, string}]}.
392+
358393
{mapping,
359394
"auth_oauth2.resource_servers.$name.oauth_provider_id",
360395
"rabbitmq_auth_backend_oauth2.resource_servers",

deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl

Lines changed: 134 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,145 @@
77

88
-module(rabbit_oauth2_schema).
99

10+
-define(AUTH_OAUTH2, "auth_oauth2").
11+
-define(SCOPE_ALIASES, "scope_aliases").
12+
-define(RESOURCE_SERVERS, "resource_servers").
13+
-define(OAUTH_PROVIDERS, "oauth_providers").
14+
-define(SIGNING_KEYS, "signing_keys").
15+
-define(AUTH_OAUTH2_SCOPE_ALIASES, ?AUTH_OAUTH2 ++ "." ++ ?SCOPE_ALIASES).
16+
-define(AUTH_OAUTH2_RESOURCE_SERVERS, ?AUTH_OAUTH2 ++ "." ++ ?RESOURCE_SERVERS).
17+
-define(AUTH_OAUTH2_OAUTH_PROVIDERS, ?AUTH_OAUTH2 ++ "." ++ ?OAUTH_PROVIDERS).
18+
-define(AUTH_OAUTH2_SIGNING_KEYS, ?AUTH_OAUTH2 ++ "." ++ ?SIGNING_KEYS).
1019

1120
-export([
1221
translate_oauth_providers/1,
1322
translate_resource_servers/1,
1423
translate_signing_keys/1,
15-
translate_endpoint_params/2
24+
translate_endpoint_params/2,
25+
translate_scope_aliases/1
1626
]).
1727

1828
extract_key_as_binary({Name,_}) -> list_to_binary(Name).
1929
extract_value({_Name,V}) -> V.
2030

31+
-spec translate_scope_aliases([{list(), binary()}]) -> map().
32+
translate_scope_aliases(Conf) ->
33+
Settings = cuttlefish_variable:filter_by_prefix(
34+
?AUTH_OAUTH2_SCOPE_ALIASES, Conf),
35+
maps:merge(extract_scope_alias_as_map(Settings),
36+
extract_scope_aliases_as_list_of_alias_scope_props(Settings)).
37+
38+
convert_space_separated_string_to_list_of_binaries(String) ->
39+
[ list_to_binary(V) || V <- string:tokens(String, " ")].
40+
41+
extract_scope_alias_as_map(Settings) ->
42+
maps:from_list([{
43+
list_to_binary(Alias),
44+
convert_space_separated_string_to_list_of_binaries(Scope)
45+
}
46+
|| {[?AUTH_OAUTH2, ?SCOPE_ALIASES, Alias], Scope} <- Settings ]).
47+
48+
extract_scope_aliases_as_list_of_alias_scope_props(Settings) ->
49+
KeyFun = fun extract_key_as_binary/1,
50+
ValueFun = fun extract_value/1,
51+
52+
List0 = [{Index, {list_to_atom(Attr), V}}
53+
|| {[?AUTH_OAUTH2, ?SCOPE_ALIASES, Index, Attr], V} <- Settings ],
54+
List1 = maps:to_list(maps:groups_from_list(KeyFun, ValueFun, List0)),
55+
List2 = [extract_scope_alias_mapping(Proplist) || {_, Proplist} <- List1],
56+
maps:from_list([ V || V <- List2, V =/= {}]).
57+
58+
extract_scope_alias_mapping(Proplist) ->
59+
Alias =
60+
case proplists:get_value(alias, Proplist) of
61+
undefined -> {error, missing_alias_attribute};
62+
A -> list_to_binary(A)
63+
end,
64+
Scope =
65+
case proplists:get_value(scope, Proplist) of
66+
undefined -> {error, missing_scope_attribute};
67+
S -> convert_space_separated_string_to_list_of_binaries(S)
68+
end,
69+
case {Alias, Scope} of
70+
{{error, _}, _} ->
71+
cuttlefish:warn(
72+
"Skipped scope_aliases due to missing alias attribute"),
73+
{};
74+
{_, {error, _}} ->
75+
cuttlefish:warn(
76+
"Skipped scope_aliases due to missing scope attribute"),
77+
{};
78+
_ = V -> V
79+
end.
80+
81+
extract_resource_server_scope_aliases_as_list_of_props(Settings) ->
82+
KeyFun = fun extract_key_as_binary/1,
83+
ValueFun = fun extract_value/1,
84+
85+
List0 = [
86+
{
87+
Name,
88+
{Index, {list_to_atom(Attr), V}}
89+
} ||
90+
{[
91+
?AUTH_OAUTH2, ?RESOURCE_SERVERS, Name, ?SCOPE_ALIASES,
92+
Index, Attr
93+
], V
94+
} <- Settings ],
95+
Map0 = maps:groups_from_list(KeyFun, ValueFun, List0),
96+
97+
Map4 = maps:map(fun (_, L) ->
98+
Map2 = maps:map(fun (_, L2) -> extract_scope_alias_mapping(L2) end,
99+
maps:groups_from_list(KeyFun, ValueFun, L)),
100+
Map3 = maps:filter(fun (_,V) -> V =/= {} end, Map2),
101+
[{scope_aliases, maps:from_list([ V || {_, V} <- maps:to_list(Map3)])}]
102+
end, Map0),
103+
104+
Map4.
105+
106+
extract_resource_server_scope_aliases_as_map(Settings) ->
107+
KeyFun = fun extract_key_as_binary/1,
108+
ValueFun = fun extract_value/1,
109+
110+
List0 = [
111+
{
112+
Name,
113+
{
114+
list_to_binary(Alias),
115+
convert_space_separated_string_to_list_of_binaries(Scope)
116+
}
117+
} ||
118+
{[
119+
?AUTH_OAUTH2, ?RESOURCE_SERVERS, Name, ?SCOPE_ALIASES,
120+
Alias
121+
], Scope
122+
} <- Settings ],
123+
Map0 = maps:groups_from_list(KeyFun, ValueFun, List0),
124+
maps:map(fun (_, L) -> [{scope_aliases, maps:from_list(L)}] end, Map0).
125+
21126
-spec translate_resource_servers([{list(), binary()}]) -> map().
22127
translate_resource_servers(Conf) ->
23-
Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.resource_servers",
24-
Conf),
128+
Settings = cuttlefish_variable:filter_by_prefix(
129+
?AUTH_OAUTH2_RESOURCE_SERVERS, Conf),
25130
Map = merge_list_of_maps([
26131
extract_resource_server_properties(Settings),
27-
extract_resource_server_preferred_username_claims(Settings)
132+
extract_resource_server_preferred_username_claims(Settings),
133+
extract_resource_server_scope_aliases_as_list_of_props(Settings),
134+
extract_resource_server_scope_aliases_as_map(Settings)
28135
]),
29136
Map0 = maps:map(fun(K,V) ->
30137
case proplists:get_value(id, V) of
31138
undefined -> V ++ [{id, K}];
32139
_ -> V
33140
end end, Map),
34141
ResourceServers = maps:values(Map0),
35-
lists:foldl(fun(Elem,AccMap) ->
36-
maps:put(proplists:get_value(id, Elem), Elem, AccMap) end, #{},
37-
ResourceServers).
142+
lists:foldl(fun(Elem,AccMap)-> maps:put(proplists:get_value(id, Elem),
143+
Elem, AccMap) end, #{}, ResourceServers).
38144

39145
-spec translate_oauth_providers([{list(), binary()}]) -> map().
40146
translate_oauth_providers(Conf) ->
41-
Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.oauth_providers",
42-
Conf),
147+
Settings = cuttlefish_variable:filter_by_prefix(
148+
?AUTH_OAUTH2_OAUTH_PROVIDERS, Conf),
43149

44150
merge_list_of_maps([
45151
extract_oauth_providers_properties(Settings),
@@ -52,8 +158,8 @@ translate_oauth_providers(Conf) ->
52158

53159
-spec translate_signing_keys([{list(), binary()}]) -> map().
54160
translate_signing_keys(Conf) ->
55-
Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.signing_keys",
56-
Conf),
161+
Settings = cuttlefish_variable:filter_by_prefix(
162+
?AUTH_OAUTH2_SIGNING_KEYS, Conf),
57163
ListOfKidPath = lists:map(fun({Id, Path}) -> {
58164
list_to_binary(lists:last(Id)), Path} end, Settings),
59165
translate_list_of_signing_keys(ListOfKidPath).
@@ -66,9 +172,9 @@ translate_list_of_signing_keys(ListOfKidPath) ->
66172
{ok, Bin} ->
67173
string:trim(Bin, trailing, "\n");
68174
_Error ->
69-
%% this throws and makes Cuttlefish treak the key as invalid
70-
cuttlefish:invalid("file does not exist or cannot be " ++
71-
"read by the node")
175+
cuttlefish:invalid(io_lib:format(
176+
"File ~p does not exist or cannot be read by the node",
177+
[Path]))
72178
end
73179
end,
74180
maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end,
@@ -87,7 +193,6 @@ validator_file_exists(Attr, Filename) ->
87193
{ok, _} ->
88194
Filename;
89195
_Error ->
90-
%% this throws and makes Cuttlefish treak the key as invalid
91196
cuttlefish:invalid(io_lib:format(
92197
"Invalid attribute (~p) value: file ~p does not exist or " ++
93198
"cannot be read by the node", [Attr, Filename]))
@@ -111,21 +216,23 @@ validator_https_uri(Attr, Uri) when is_list(Uri) ->
111216
true -> Uri;
112217
false ->
113218
cuttlefish:invalid(io_lib:format(
114-
"Invalid attribute (~p) value: uri ~p must be a valid https uri",
115-
[Attr, Uri]))
219+
"Invalid attribute (~p) value: uri ~p must be a valid " ++
220+
"https uri", [Attr, Uri]))
116221
end.
117222

118223
merge_list_of_maps(ListOfMaps) ->
119-
lists:foldl(fun(Elem, AccIn) -> maps:merge_with(fun(_K,V1,V2) -> V1 ++ V2 end,
120-
Elem, AccIn) end, #{}, ListOfMaps).
224+
lists:foldl(fun(Elem, AccIn) -> maps:merge_with(
225+
fun(_K,V1,V2) -> V1 ++ V2 end, Elem, AccIn) end, #{}, ListOfMaps).
121226

122227
extract_oauth_providers_properties(Settings) ->
123228
KeyFun = fun extract_key_as_binary/1,
124229
ValueFun = fun extract_value/1,
125230

126-
OAuthProviders = [
127-
{Name, mapOauthProviderProperty({list_to_atom(Key), list_to_binary(V)})}
128-
|| {["auth_oauth2", "oauth_providers", Name, Key], V} <- Settings],
231+
OAuthProviders = [{Name, mapOauthProviderProperty(
232+
{
233+
list_to_atom(Key),
234+
list_to_binary(V)})
235+
} || {[?AUTH_OAUTH2, ?OAUTH_PROVIDERS, Name, Key], V} <- Settings ],
129236
maps:groups_from_list(KeyFun, ValueFun, OAuthProviders).
130237

131238

@@ -134,7 +241,7 @@ extract_resource_server_properties(Settings) ->
134241
ValueFun = fun extract_value/1,
135242

136243
OAuthProviders = [{Name, {list_to_atom(Key), list_to_binary(V)}}
137-
|| {["auth_oauth2","resource_servers", Name, Key], V} <- Settings ],
244+
|| {[?AUTH_OAUTH2, ?RESOURCE_SERVERS, Name, Key], V} <- Settings ],
138245
maps:groups_from_list(KeyFun, ValueFun, OAuthProviders).
139246

140247
mapOauthProviderProperty({Key, Value}) ->
@@ -156,7 +263,7 @@ extract_oauth_providers_https(Settings) ->
156263
ExtractProviderNameFun = fun extract_key_as_binary/1,
157264

158265
AttributesPerProvider = [{Name, mapHttpProperty({list_to_atom(Key), V})} ||
159-
{["auth_oauth2","oauth_providers", Name, "https", Key], V} <- Settings ],
266+
{[?AUTH_OAUTH2, ?OAUTH_PROVIDERS, Name, "https", Key], V} <- Settings ],
160267

161268
maps:map(fun(_K,V)-> [{https, V}] end,
162269
maps:groups_from_list(ExtractProviderNameFun, fun({_, V}) -> V end,
@@ -172,7 +279,7 @@ extract_oauth_providers_algorithm(Settings) ->
172279
KeyFun = fun extract_key_as_binary/1,
173280

174281
IndexedAlgorithms = [{Name, {Index, list_to_binary(V)}} ||
175-
{["auth_oauth2","oauth_providers", Name, "algorithms", Index], V}
282+
{[?AUTH_OAUTH2, ?OAUTH_PROVIDERS, Name, "algorithms", Index], V}
176283
<- Settings ],
177284
SortedAlgorithms = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end,
178285
IndexedAlgorithms),
@@ -184,7 +291,7 @@ extract_resource_server_preferred_username_claims(Settings) ->
184291
KeyFun = fun extract_key_as_binary/1,
185292

186293
IndexedClaims = [{Name, {Index, list_to_binary(V)}} ||
187-
{["auth_oauth2","resource_servers", Name, "preferred_username_claims",
294+
{[?AUTH_OAUTH2, ?RESOURCE_SERVERS, Name, "preferred_username_claims",
188295
Index], V} <- Settings ],
189296
SortedClaims = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end,
190297
IndexedClaims),
@@ -205,7 +312,7 @@ extract_oauth_providers_signing_keys(Settings) ->
205312
KeyFun = fun extract_key_as_binary/1,
206313

207314
IndexedSigningKeys = [{Name, {list_to_binary(Kid), list_to_binary(V)}} ||
208-
{["auth_oauth2","oauth_providers", Name, "signing_keys", Kid], V}
315+
{[?AUTH_OAUTH2, ?OAUTH_PROVIDERS, Name, ?SIGNING_KEYS, Kid], V}
209316
<- Settings ],
210317
maps:map(fun(_K,V)-> [{signing_keys, translate_list_of_signing_keys(V)}] end,
211318
maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedSigningKeys)).

0 commit comments

Comments
 (0)