|
28 | 28 | get_scope/1, set_scope/2, |
29 | 29 | resolve_resource_server/1]). |
30 | 30 |
|
31 | | --import(rabbit_oauth2_keycloak, [has_keycloak_scopes/1, extract_scopes_from_keycloak_format/1]). |
32 | | --import(rabbit_oauth2_rar, [extract_scopes_from_rich_auth_request/2, has_rich_auth_request_scopes/1]). |
| 31 | +-import(rabbit_oauth2_rar, [extract_scopes_from_rich_auth_request/2]). |
33 | 32 |
|
34 | 33 | -import(rabbit_oauth2_scope, [filter_matching_scope_prefix_and_drop_it/2]). |
35 | 34 |
|
@@ -229,79 +228,142 @@ check_token(Token, {ResourceServer, InternalOAuthProvider}) -> |
229 | 228 | {false, _} -> {refused, signature_invalid} |
230 | 229 | end. |
231 | 230 |
|
| 231 | +extract_scopes_from_scope_claim(Payload) -> |
| 232 | + case maps:find(?SCOPE_JWT_FIELD, Payload) of |
| 233 | + {ok, Bin} when is_binary(Bin) -> |
| 234 | + maps:put(?SCOPE_JWT_FIELD, |
| 235 | + binary:split(Bin, <<" ">>, [global, trim_all]), |
| 236 | + Payload); |
| 237 | + _ -> Payload |
| 238 | + end. |
| 239 | + |
232 | 240 | -spec normalize_token_scope( |
233 | 241 | ResourceServer :: resource_server(), DecodedToken :: decoded_jwt_token()) -> map(). |
234 | 242 | normalize_token_scope(ResourceServer, Payload) -> |
235 | | - Payload0 = maps:map(fun(K, V) -> |
236 | | - case K of |
237 | | - ?SCOPE_JWT_FIELD when is_binary(V) -> |
238 | | - binary:split(V, <<" ">>, [global, trim_all]); |
239 | | - _ -> V |
240 | | - end |
241 | | - end, Payload), |
242 | | - |
243 | | - Payload1 = case has_additional_scopes_key(ResourceServer, Payload0) of |
244 | | - true -> extract_scopes_from_additional_scopes_key(ResourceServer, Payload0); |
245 | | - false -> Payload0 |
246 | | - end, |
247 | | - |
248 | | - Payload2 = case has_keycloak_scopes(Payload1) of |
249 | | - true -> extract_scopes_from_keycloak_format(Payload1); |
250 | | - false -> Payload1 |
251 | | - end, |
252 | | - |
253 | | - Payload3 = case ResourceServer#resource_server.scope_aliases of |
254 | | - undefined -> Payload2; |
255 | | - ScopeAliases -> extract_scopes_using_scope_aliases(ScopeAliases, Payload2) |
256 | | - end, |
257 | | - |
258 | | - Payload4 = case has_rich_auth_request_scopes(Payload3) of |
259 | | - true -> extract_scopes_from_rich_auth_request(ResourceServer, Payload3); |
260 | | - false -> Payload3 |
261 | | - end, |
| 243 | + |
| 244 | + Payload1 = extract_scopes_from_rich_auth_request(ResourceServer, |
| 245 | + extract_scopes_using_scope_aliases(ResourceServer, |
| 246 | + extract_scopes_from_additional_scopes_key(ResourceServer, |
| 247 | + extract_scopes_from_scope_claim(Payload)))), |
262 | 248 |
|
263 | 249 | FilteredScopes = filter_matching_scope_prefix_and_drop_it( |
264 | | - get_scope(Payload4), ResourceServer#resource_server.scope_prefix), |
265 | | - set_scope(FilteredScopes, Payload4). |
266 | | - |
| 250 | + get_scope(Payload1), ResourceServer#resource_server.scope_prefix), |
| 251 | + set_scope(FilteredScopes, Payload1). |
267 | 252 |
|
268 | 253 | -spec extract_scopes_using_scope_aliases( |
269 | | - ScopeAliasMapping :: map(), Payload :: map()) -> map(). |
270 | | -extract_scopes_using_scope_aliases(ScopeAliasMapping, Payload) -> |
271 | | - Scopes0 = get_scope(Payload), |
272 | | - Scopes = rabbit_data_coercion:to_list_of_binaries(Scopes0), |
273 | | - %% for all scopes, look them up in the scope alias map, and if they are |
274 | | - %% present, add the alias to the final scope list. Note that we also preserve |
275 | | - %% the original scopes, it should not hurt. |
276 | | - ExpandedScopes = |
277 | | - lists:foldl(fun(ScopeListItem, Acc) -> |
278 | | - case maps:get(ScopeListItem, ScopeAliasMapping, undefined) of |
279 | | - undefined -> |
280 | | - Acc; |
281 | | - MappedList when is_list(MappedList) -> |
282 | | - Binaries = rabbit_data_coercion:to_list_of_binaries(MappedList), |
283 | | - Acc ++ Binaries; |
284 | | - Value -> |
285 | | - Binaries = rabbit_data_coercion:to_list_of_binaries(Value), |
286 | | - Acc ++ Binaries |
287 | | - end |
288 | | - end, Scopes, Scopes), |
289 | | - set_scope(ExpandedScopes, Payload). |
290 | | - |
291 | | --spec has_additional_scopes_key( |
292 | | - ResourceServer :: resource_server(), Payload :: map()) -> boolean(). |
293 | | -has_additional_scopes_key(ResourceServer, Payload) when is_map(Payload) -> |
294 | | - case ResourceServer#resource_server.additional_scopes_key of |
295 | | - undefined -> false; |
296 | | - ScopeKey -> maps:is_key(ScopeKey, Payload) |
297 | | - end. |
| 254 | + ResourceServer :: resource_server(), Payload :: map()) -> map(). |
| 255 | +extract_scopes_using_scope_aliases( |
| 256 | + #resource_server{scope_aliases = ScopeAliasMapping}, Payload) |
| 257 | + when is_map(ScopeAliasMapping) -> |
| 258 | + Scopes0 = get_scope(Payload), |
| 259 | + Scopes = rabbit_data_coercion:to_list_of_binaries(Scopes0), |
| 260 | + %% for all scopes, look them up in the scope alias map, and if they are |
| 261 | + %% present, add the alias to the final scope list. Note that we also preserve |
| 262 | + %% the original scopes, it should not hurt. |
| 263 | + ExpandedScopes = |
| 264 | + lists:foldl(fun(ScopeListItem, Acc) -> |
| 265 | + case maps:get(ScopeListItem, ScopeAliasMapping, undefined) of |
| 266 | + undefined -> |
| 267 | + Acc; |
| 268 | + MappedList when is_list(MappedList) -> |
| 269 | + Binaries = rabbit_data_coercion:to_list_of_binaries(MappedList), |
| 270 | + Acc ++ Binaries; |
| 271 | + Value -> |
| 272 | + Binaries = rabbit_data_coercion:to_list_of_binaries(Value), |
| 273 | + Acc ++ Binaries |
| 274 | + end |
| 275 | + end, Scopes, Scopes), |
| 276 | + set_scope(ExpandedScopes, Payload); |
| 277 | +extract_scopes_using_scope_aliases(_, Payload) -> Payload. |
| 278 | + |
| 279 | +%% Path is a binary expression which is a plain word like <<"roles">> |
| 280 | +%% or +1 word separated by . like <<"authorization.permissions.scopes">> |
| 281 | +%% The Payload is a map. |
| 282 | +%% Using the path <<"authorization.permissions.scopes">> as an example |
| 283 | +%% 1. lookup the key <<"authorization">> in the Payload |
| 284 | +%% 2. if it is found, the next map to use as payload is the value found from the key <<"authorization">> |
| 285 | +%% 3. lookup the key <<"permissions">> in the previous map |
| 286 | +%% 4. if it is found, it may be a map or a list of maps. |
| 287 | +%% 5. if it is a list of maps, iterate each element in the list |
| 288 | +%% 6. for each element in the list, which should be a map, find the key <<"scopes">> |
| 289 | +%% 7. because there are no more words/keys, return a list of all the values found |
| 290 | +%% associated to the word <<"scopes">> |
| 291 | +extract_token_value(R, Payload, Path, ValueMapperFun) |
| 292 | + when is_map(Payload), is_binary(Path), is_function(ValueMapperFun) -> |
| 293 | + extract_token_value_from_map(R, Payload, [], split_path(Path), ValueMapperFun); |
| 294 | +extract_token_value(_, _, _, _) -> |
| 295 | + []. |
| 296 | + |
| 297 | +extract_scope_list_from_token_value(_R, List) when is_list(List) -> List; |
| 298 | +extract_scope_list_from_token_value(_R, Binary) when is_binary(Binary) -> |
| 299 | + binary:split(Binary, <<" ">>, [global, trim_all]); |
| 300 | +extract_scope_list_from_token_value(#resource_server{id = ResourceServerId}, Map) when is_map(Map) -> |
| 301 | + case maps:get(ResourceServerId, Map, undefined) of |
| 302 | + undefined -> []; |
| 303 | + Ks when is_list(Ks) -> |
| 304 | + [erlang:iolist_to_binary([ResourceServerId, <<".">>, K]) || K <- Ks]; |
| 305 | + ClaimBin when is_binary(ClaimBin) -> |
| 306 | + UnprefixedClaims = binary:split(ClaimBin, <<" ">>, [global, trim_all]), |
| 307 | + [erlang:iolist_to_binary([ResourceServerId, <<".">>, K]) || K <- UnprefixedClaims]; |
| 308 | + _ -> [] |
| 309 | + end; |
| 310 | +extract_scope_list_from_token_value(_, _) -> []. |
| 311 | + |
| 312 | +extract_token_value_from_map(_, _Map, Acc, [], _Mapper) -> |
| 313 | + Acc; |
| 314 | +extract_token_value_from_map(R, Map, Acc, [KeyStr], Mapper) when is_map(Map) -> |
| 315 | + case maps:find(KeyStr, Map) of |
| 316 | + {ok, Value} -> Acc ++ Mapper(R, Value); |
| 317 | + error -> Acc |
| 318 | + end; |
| 319 | +extract_token_value_from_map(R, Map, Acc, [KeyStr | Rest], Mapper) when is_map(Map) -> |
| 320 | + case maps:find(KeyStr, Map) of |
| 321 | + {ok, M} when is_map(M) -> extract_token_value_from_map(R, M, Acc, Rest, Mapper); |
| 322 | + {ok, L} when is_list(L) -> extract_token_value_from_list(R, L, Acc, Rest, Mapper); |
| 323 | + {ok, Value} when Rest =:= [] -> Acc ++ Mapper(R, Value); |
| 324 | + _ -> Acc |
| 325 | + end; |
| 326 | +extract_token_value_from_map(_, _, Acc, _, _Mapper) -> |
| 327 | + Acc. |
| 328 | + |
| 329 | +extract_token_value_from_list(_, [], Acc, [], _Mapper) -> |
| 330 | + Acc; |
| 331 | +extract_token_value_from_list(_, [], Acc, [_KeyStr | _Rest], _Mapper) -> |
| 332 | + Acc; |
| 333 | +extract_token_value_from_list(R, [H | T], Acc, [KeyStr | Rest] = KeyList, Mapper) when is_map(H) -> |
| 334 | + NewAcc = case maps:find(KeyStr, H) of |
| 335 | + {ok, Map} when is_map(Map) -> extract_token_value_from_map(R, Map, Acc, Rest, Mapper); |
| 336 | + {ok, List} when is_list(List) -> extract_token_value_from_list(R, List, Acc, Rest, Mapper); |
| 337 | + {ok, Value} -> Acc++Mapper(R, Value); |
| 338 | + _ -> Acc |
| 339 | + end, |
| 340 | + extract_token_value_from_list(R, T, NewAcc, KeyList, Mapper); |
| 341 | + |
| 342 | +extract_token_value_from_list(R, [E | T], Acc, [], Mapper) -> |
| 343 | + extract_token_value_from_list(R, T, Acc++Mapper(R, E), [], Mapper); |
| 344 | +extract_token_value_from_list(R, [E | _T] = L, Acc, KeyList, Mapper) when is_map(E) -> |
| 345 | + extract_token_value_from_list(R, L, Acc, KeyList, Mapper); |
| 346 | +extract_token_value_from_list(R, [_ | T], Acc, KeyList, Mapper) -> |
| 347 | + extract_token_value_from_list(R, T, Acc, KeyList, Mapper). |
| 348 | + |
| 349 | + |
| 350 | +split_path(Path) when is_binary(Path) -> |
| 351 | + binary:split(Path, <<".">>, [global, trim_all]). |
| 352 | + |
298 | 353 |
|
299 | 354 | -spec extract_scopes_from_additional_scopes_key( |
300 | 355 | ResourceServer :: resource_server(), Payload :: map()) -> map(). |
301 | | -extract_scopes_from_additional_scopes_key(ResourceServer, Payload) -> |
302 | | - Claim = maps:get(ResourceServer#resource_server.additional_scopes_key, Payload), |
303 | | - AdditionalScopes = extract_additional_scopes(ResourceServer, Claim), |
304 | | - set_scope(AdditionalScopes ++ get_scope(Payload), Payload). |
| 356 | +extract_scopes_from_additional_scopes_key( |
| 357 | + #resource_server{additional_scopes_key = Key} = ResourceServer, Payload) |
| 358 | + when is_list(Key) or is_binary(Key) -> |
| 359 | + Paths = case Key of |
| 360 | + B when is_binary(B) -> binary:split(B, <<" ">>, [global, trim_all]); |
| 361 | + L when is_list(L) -> L |
| 362 | + end, |
| 363 | + AdditionalScopes = [ extract_token_value(ResourceServer, |
| 364 | + Payload, Path, fun extract_scope_list_from_token_value/2) || Path <- Paths], |
| 365 | + set_scope(lists:flatten(AdditionalScopes) ++ get_scope(Payload), Payload); |
| 366 | +extract_scopes_from_additional_scopes_key(_, Payload) -> Payload. |
305 | 367 |
|
306 | 368 | extract_additional_scopes(ResourceServer, ComplexClaim) -> |
307 | 369 | ResourceServerId = ResourceServer#resource_server.id, |
|
0 commit comments