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-2021 VMware, Inc. or its affiliates.  All rights reserved.
6%%
7
8-module(rabbit_auth_backend_internal).
9-include_lib("rabbit_common/include/rabbit.hrl").
10
11-behaviour(rabbit_authn_backend).
12-behaviour(rabbit_authz_backend).
13
14-export([user_login_authentication/2, user_login_authorization/2,
15         check_vhost_access/3, check_resource_access/4, check_topic_access/4]).
16
17-export([add_user/3, delete_user/2, lookup_user/1, exists/1,
18         change_password/3, clear_password/2,
19         hash_password/2, change_password_hash/2, change_password_hash/3,
20         set_tags/3, set_permissions/6, clear_permissions/3,
21         set_topic_permissions/6, clear_topic_permissions/3, clear_topic_permissions/4,
22         add_user_sans_validation/3, put_user/2, put_user/3]).
23
24-export([set_user_limits/3, clear_user_limits/3, is_over_connection_limit/1,
25         is_over_channel_limit/1, get_user_limits/0, get_user_limits/1]).
26
27-export([user_info_keys/0, perms_info_keys/0,
28         user_perms_info_keys/0, vhost_perms_info_keys/0,
29         user_vhost_perms_info_keys/0, all_users/0,
30         list_users/0, list_users/2, list_permissions/0,
31         list_user_permissions/1, list_user_permissions/3,
32         list_topic_permissions/0,
33         list_vhost_permissions/1, list_vhost_permissions/3,
34         list_user_vhost_permissions/2,
35         list_user_topic_permissions/1, list_vhost_topic_permissions/1, list_user_vhost_topic_permissions/2]).
36
37-export([state_can_expire/0]).
38
39%% for testing
40-export([hashing_module_for_user/1, expand_topic_permission/2]).
41
42-import(rabbit_data_coercion, [to_atom/1, to_list/1, to_binary/1]).
43
44%%----------------------------------------------------------------------------
45
46-type regexp() :: binary().
47
48%%----------------------------------------------------------------------------
49%% Implementation of rabbit_auth_backend
50
51%% Returns a password hashing module for the user record provided. If
52%% there is no information in the record, we consider it to be legacy
53%% (inserted by a version older than 3.6.0) and fall back to MD5, the
54%% now obsolete hashing function.
55hashing_module_for_user(User) ->
56    ModOrUndefined = internal_user:get_hashing_algorithm(User),
57    rabbit_password:hashing_mod(ModOrUndefined).
58
59-define(BLANK_PASSWORD_REJECTION_MESSAGE,
60        "user '~s' attempted to log in with a blank password, which is prohibited by the internal authN backend. "
61        "To use TLS/x509 certificate-based authentication, see the rabbitmq_auth_mechanism_ssl plugin and configure the client to use the EXTERNAL authentication mechanism. "
62        "Alternatively change the password for the user to be non-blank.").
63
64%% For cases when we do not have a set of credentials,
65%% namely when x509 (TLS) certificates are used. This should only be
66%% possible when the EXTERNAL authentication mechanism is used, see
67%% rabbit_auth_mechanism_plain:handle_response/2 and rabbit_reader:auth_phase/2.
68user_login_authentication(Username, []) ->
69    internal_check_user_login(Username, fun(_) -> true end);
70%% For cases when we do have a set of credentials. rabbit_auth_mechanism_plain:handle_response/2
71%% performs initial validation.
72user_login_authentication(Username, AuthProps) ->
73    case lists:keyfind(password, 1, AuthProps) of
74        {password, <<"">>} ->
75            {refused, ?BLANK_PASSWORD_REJECTION_MESSAGE,
76             [Username]};
77        {password, ""} ->
78            {refused, ?BLANK_PASSWORD_REJECTION_MESSAGE,
79             [Username]};
80        {password, Cleartext} ->
81            internal_check_user_login(
82              Username,
83              fun(User) ->
84                  case internal_user:get_password_hash(User) of
85                      <<Salt:4/binary, Hash/binary>> ->
86                          Hash =:= rabbit_password:salted_hash(
87                              hashing_module_for_user(User), Salt, Cleartext);
88                      _ ->
89                          false
90                  end
91              end);
92        false -> exit({unknown_auth_props, Username, AuthProps})
93    end.
94
95state_can_expire() -> false.
96
97user_login_authorization(Username, _AuthProps) ->
98    case user_login_authentication(Username, []) of
99        {ok, #auth_user{impl = Impl, tags = Tags}} -> {ok, Impl, Tags};
100        Else                                       -> Else
101    end.
102
103internal_check_user_login(Username, Fun) ->
104    Refused = {refused, "user '~s' - invalid credentials", [Username]},
105    case lookup_user(Username) of
106        {ok, User} ->
107            Tags = internal_user:get_tags(User),
108            case Fun(User) of
109                true -> {ok, #auth_user{username = Username,
110                                        tags     = Tags,
111                                        impl     = none}};
112                _    -> Refused
113            end;
114        {error, not_found} ->
115            Refused
116    end.
117
118check_vhost_access(#auth_user{username = Username}, VHostPath, _AuthzData) ->
119    case mnesia:dirty_read({rabbit_user_permission,
120                            #user_vhost{username     = Username,
121                                        virtual_host = VHostPath}}) of
122        []   -> false;
123        [_R] -> true
124    end.
125
126check_resource_access(#auth_user{username = Username},
127                      #resource{virtual_host = VHostPath, name = Name},
128                      Permission,
129                      _AuthContext) ->
130    case mnesia:dirty_read({rabbit_user_permission,
131                            #user_vhost{username     = Username,
132                                        virtual_host = VHostPath}}) of
133        [] ->
134            false;
135        [#user_permission{permission = P}] ->
136            PermRegexp = case element(permission_index(Permission), P) of
137                             %% <<"^$">> breaks Emacs' erlang mode
138                             <<"">> -> <<$^, $$>>;
139                             RE     -> RE
140                         end,
141            case re:run(Name, PermRegexp, [{capture, none}]) of
142                match    -> true;
143                nomatch  -> false
144            end
145    end.
146
147check_topic_access(#auth_user{username = Username},
148                   #resource{virtual_host = VHostPath, name = Name, kind = topic},
149                   Permission,
150                   Context) ->
151    case mnesia:dirty_read({rabbit_topic_permission,
152        #topic_permission_key{user_vhost = #user_vhost{username     = Username,
153                                                       virtual_host = VHostPath},
154                                                       exchange     = Name
155                             }}) of
156        [] ->
157            true;
158        [#topic_permission{permission = P}] ->
159            PermRegexp = case element(permission_index(Permission), P) of
160                             %% <<"^$">> breaks Emacs' erlang mode
161                             <<"">> -> <<$^, $$>>;
162                             RE     -> RE
163                         end,
164            PermRegexpExpanded = expand_topic_permission(
165                PermRegexp,
166                maps:get(variable_map, Context, undefined)
167            ),
168            case re:run(maps:get(routing_key, Context), PermRegexpExpanded, [{capture, none}]) of
169                match    -> true;
170                nomatch  -> false
171            end
172    end.
173
174expand_topic_permission(Permission, ToExpand) when is_map(ToExpand) ->
175    Opening = <<"{">>,
176    Closing = <<"}">>,
177    ReplaceFun = fun(K, V, Acc) ->
178                    Placeholder = <<Opening/binary, K/binary, Closing/binary>>,
179                    binary:replace(Acc, Placeholder, V, [global])
180                 end,
181    maps:fold(ReplaceFun, Permission, ToExpand);
182expand_topic_permission(Permission, _ToExpand) ->
183    Permission.
184
185permission_index(configure) -> #permission.configure;
186permission_index(write)     -> #permission.write;
187permission_index(read)      -> #permission.read.
188
189%%----------------------------------------------------------------------------
190%% Manipulation of the user database
191
192validate_credentials(Username, Password) ->
193    rabbit_credential_validation:validate(Username, Password).
194
195validate_and_alternate_credentials(Username, Password, ActingUser, Fun) ->
196    case validate_credentials(Username, Password) of
197        ok           ->
198            Fun(Username, Password, ActingUser);
199        {error, Err} ->
200            rabbit_log:error("Credential validation for '~s' failed!", [Username]),
201            {error, Err}
202    end.
203
204-spec add_user(rabbit_types:username(), rabbit_types:password(),
205               rabbit_types:username()) -> 'ok' | {'error', string()}.
206
207add_user(Username, Password, ActingUser) ->
208    validate_and_alternate_credentials(Username, Password, ActingUser,
209                                       fun add_user_sans_validation/3).
210
211add_user_sans_validation(Username, Password, ActingUser) ->
212    rabbit_log:debug("Asked to create a new user '~s', password length in bytes: ~p", [Username, bit_size(Password)]),
213    %% hash_password will pick the hashing function configured for us
214    %% but we also need to store a hint as part of the record, so we
215    %% retrieve it here one more time
216    HashingMod = rabbit_password:hashing_mod(),
217    PasswordHash = hash_password(HashingMod, Password),
218    User = internal_user:create_user(Username, PasswordHash, HashingMod),
219    try
220        R = rabbit_misc:execute_mnesia_transaction(
221          fun () ->
222                  case mnesia:wread({rabbit_user, Username}) of
223                      [] ->
224                          ok = mnesia:write(rabbit_user, User, write);
225                      _ ->
226                          mnesia:abort({user_already_exists, Username})
227                  end
228          end),
229        rabbit_log:info("Created user '~s'", [Username]),
230        rabbit_event:notify(user_created, [{name, Username},
231                                           {user_who_performed_action, ActingUser}]),
232        R
233    catch
234        throw:{error, {user_already_exists, _}} = Error ->
235            rabbit_log:warning("Failed to add user '~s': the user already exists", [Username]),
236            throw(Error);
237        Class:Error:Stacktrace ->
238            rabbit_log:warning("Failed to add user '~s': ~p", [Username, Error]),
239            erlang:raise(Class, Error, Stacktrace)
240    end .
241
242-spec delete_user(rabbit_types:username(), rabbit_types:username()) -> 'ok'.
243
244delete_user(Username, ActingUser) ->
245    rabbit_log:debug("Asked to delete user '~s'", [Username]),
246    try
247        R = rabbit_misc:execute_mnesia_transaction(
248          rabbit_misc:with_user(
249            Username,
250            fun () ->
251                    ok = mnesia:delete({rabbit_user, Username}),
252                    [ok = mnesia:delete_object(
253                            rabbit_user_permission, R, write) ||
254                        R <- mnesia:match_object(
255                               rabbit_user_permission,
256                               #user_permission{user_vhost = #user_vhost{
257                                                  username = Username,
258                                                  virtual_host = '_'},
259                                                permission = '_'},
260                               write)],
261                    UserTopicPermissionsQuery = match_user_vhost_topic_permission(Username, '_'),
262                    UserTopicPermissions = UserTopicPermissionsQuery(),
263                    [ok = mnesia:delete_object(rabbit_topic_permission, R, write) || R <- UserTopicPermissions],
264                    ok
265            end)),
266        rabbit_log:info("Deleted user '~s'", [Username]),
267        rabbit_event:notify(user_deleted,
268                            [{name, Username},
269                             {user_who_performed_action, ActingUser}]),
270        R
271    catch
272        throw:{error, {no_such_user, _}} = Error ->
273            rabbit_log:warning("Failed to delete user '~s': the user does not exist", [Username]),
274            throw(Error);
275        Class:Error:Stacktrace ->
276            rabbit_log:warning("Failed to delete user '~s': ~p", [Username, Error]),
277            erlang:raise(Class, Error, Stacktrace)
278    end .
279
280-spec lookup_user
281        (rabbit_types:username()) ->
282            rabbit_types:ok(internal_user:internal_user()) |
283            rabbit_types:error('not_found').
284
285lookup_user(Username) ->
286    rabbit_misc:dirty_read({rabbit_user, Username}).
287
288-spec exists(rabbit_types:username()) -> boolean().
289
290exists(Username) ->
291    case lookup_user(Username) of
292        {error, not_found} -> false;
293        _                  -> true
294    end.
295
296-spec change_password
297        (rabbit_types:username(), rabbit_types:password(), rabbit_types:username()) -> 'ok'.
298
299change_password(Username, Password, ActingUser) ->
300    validate_and_alternate_credentials(Username, Password, ActingUser,
301                                       fun change_password_sans_validation/3).
302
303change_password_sans_validation(Username, Password, ActingUser) ->
304    try
305        rabbit_log:debug("Asked to change password of user '~s', new password length in bytes: ~p", [Username, bit_size(Password)]),
306        HashingAlgorithm = rabbit_password:hashing_mod(),
307        R = change_password_hash(Username,
308                                 hash_password(rabbit_password:hashing_mod(),
309                                               Password),
310                                 HashingAlgorithm),
311        rabbit_log:info("Successfully changed password for user '~s'", [Username]),
312        rabbit_event:notify(user_password_changed,
313                            [{name, Username},
314                             {user_who_performed_action, ActingUser}]),
315        R
316    catch
317        throw:{error, {no_such_user, _}} = Error ->
318            rabbit_log:warning("Failed to change password for user '~s': the user does not exist", [Username]),
319            throw(Error);
320        Class:Error:Stacktrace ->
321            rabbit_log:warning("Failed to change password for user '~s': ~p", [Username, Error]),
322            erlang:raise(Class, Error, Stacktrace)
323    end.
324
325-spec clear_password(rabbit_types:username(), rabbit_types:username()) -> 'ok'.
326
327clear_password(Username, ActingUser) ->
328    rabbit_log:info("Clearing password for '~s'", [Username]),
329    R = change_password_hash(Username, <<"">>),
330    rabbit_event:notify(user_password_cleared,
331                        [{name, Username},
332                         {user_who_performed_action, ActingUser}]),
333    R.
334
335-spec hash_password
336        (module(), rabbit_types:password()) -> rabbit_types:password_hash().
337
338hash_password(HashingMod, Cleartext) ->
339    rabbit_password:hash(HashingMod, Cleartext).
340
341-spec change_password_hash
342        (rabbit_types:username(), rabbit_types:password_hash()) -> 'ok'.
343
344change_password_hash(Username, PasswordHash) ->
345    change_password_hash(Username, PasswordHash, rabbit_password:hashing_mod()).
346
347
348change_password_hash(Username, PasswordHash, HashingAlgorithm) ->
349    update_user(Username, fun(User) ->
350                              internal_user:set_password_hash(User,
351                                  PasswordHash, HashingAlgorithm)
352                          end).
353
354-spec set_tags(rabbit_types:username(), [atom()], rabbit_types:username()) -> 'ok'.
355
356set_tags(Username, Tags, ActingUser) ->
357    ConvertedTags = [rabbit_data_coercion:to_atom(I) || I <- Tags],
358    rabbit_log:debug("Asked to set user tags for user '~s' to ~p", [Username, ConvertedTags]),
359    try
360        R = update_user(Username, fun(User) ->
361                                     internal_user:set_tags(User, ConvertedTags)
362                                  end),
363        rabbit_log:info("Successfully set user tags for user '~s' to ~p", [Username, ConvertedTags]),
364        rabbit_event:notify(user_tags_set, [{name, Username}, {tags, ConvertedTags},
365                                            {user_who_performed_action, ActingUser}]),
366        R
367    catch
368        throw:{error, {no_such_user, _}} = Error ->
369            rabbit_log:warning("Failed to set tags for user '~s': the user does not exist", [Username]),
370            throw(Error);
371        Class:Error:Stacktrace ->
372            rabbit_log:warning("Failed to set tags for user '~s': ~p", [Username, Error]),
373            erlang:raise(Class, Error, Stacktrace)
374    end .
375
376-spec set_permissions
377        (rabbit_types:username(), rabbit_types:vhost(), regexp(), regexp(),
378         regexp(), rabbit_types:username()) ->
379            'ok'.
380
381set_permissions(Username, VirtualHost, ConfigurePerm, WritePerm, ReadPerm, ActingUser) ->
382    rabbit_log:debug("Asked to set permissions for "
383                     "'~s' in virtual host '~s' to '~s', '~s', '~s'",
384                     [Username, VirtualHost, ConfigurePerm, WritePerm, ReadPerm]),
385    lists:map(
386      fun (RegexpBin) ->
387              Regexp = binary_to_list(RegexpBin),
388              case re:compile(Regexp) of
389                  {ok, _}         -> ok;
390                  {error, Reason} ->
391                      rabbit_log:warning("Failed to set permissions for '~s' in virtual host '~s': "
392                                         "regular expression '~s' is invalid",
393                                         [Username, VirtualHost, RegexpBin]),
394                      throw({error, {invalid_regexp, Regexp, Reason}})
395              end
396      end, [ConfigurePerm, WritePerm, ReadPerm]),
397    try
398        R = rabbit_misc:execute_mnesia_transaction(
399             rabbit_vhost:with_user_and_vhost(
400                Username, VirtualHost,
401                fun () -> ok = mnesia:write(
402                                 rabbit_user_permission,
403                                 #user_permission{user_vhost = #user_vhost{
404                                                      username     = Username,
405                                                      virtual_host = VirtualHost},
406                                                  permission = #permission{
407                                                      configure  = ConfigurePerm,
408                                                      write      = WritePerm,
409                                                      read       = ReadPerm}},
410                                 write)
411                end)),
412        rabbit_log:info("Successfully set permissions for "
413                        "'~s' in virtual host '~s' to '~s', '~s', '~s'",
414                        [Username, VirtualHost, ConfigurePerm, WritePerm, ReadPerm]),
415        rabbit_event:notify(permission_created, [{user,      Username},
416                                                 {vhost,     VirtualHost},
417                                                 {configure, ConfigurePerm},
418                                                 {write,     WritePerm},
419                                                 {read,      ReadPerm},
420                                                 {user_who_performed_action, ActingUser}]),
421        R
422    catch
423        throw:{error, {no_such_vhost, _}} = Error ->
424            rabbit_log:warning("Failed to set permissions for '~s': virtual host '~s' does not exist",
425                               [Username, VirtualHost]),
426            throw(Error);
427        throw:{error, {no_such_user, _}} = Error ->
428            rabbit_log:warning("Failed to set permissions for '~s': the user does not exist",
429                               [Username]),
430            throw(Error);
431        Class:Error:Stacktrace ->
432            rabbit_log:warning("Failed to set permissions for '~s' in virtual host '~s': ~p",
433                               [Username, VirtualHost, Error]),
434            erlang:raise(Class, Error, Stacktrace)
435    end.
436
437-spec clear_permissions
438        (rabbit_types:username(), rabbit_types:vhost(), rabbit_types:username()) -> 'ok'.
439
440clear_permissions(Username, VirtualHost, ActingUser) ->
441    rabbit_log:debug("Asked to clear permissions for '~s' in virtual host '~s'",
442                     [Username, VirtualHost]),
443    try
444        R = rabbit_misc:execute_mnesia_transaction(
445          rabbit_vhost:with_user_and_vhost(
446            Username, VirtualHost,
447            fun () ->
448                    ok = mnesia:delete({rabbit_user_permission,
449                                        #user_vhost{username     = Username,
450                                                    virtual_host = VirtualHost}})
451            end)),
452        rabbit_log:info("Successfully cleared permissions for '~s' in virtual host '~s'",
453                        [Username, VirtualHost]),
454        rabbit_event:notify(permission_deleted, [{user,  Username},
455                                                 {vhost, VirtualHost},
456                                                 {user_who_performed_action, ActingUser}]),
457        R
458    catch
459        throw:{error, {no_such_vhost, _}} = Error ->
460            rabbit_log:warning("Failed to clear permissions for '~s': virtual host '~s' does not exist",
461                               [Username, VirtualHost]),
462            throw(Error);
463        throw:{error, {no_such_user, _}} = Error ->
464            rabbit_log:warning("Failed to clear permissions for '~s': the user does not exist",
465                               [Username]),
466            throw(Error);
467        Class:Error:Stacktrace ->
468            rabbit_log:warning("Failed to clear permissions for '~s' in virtual host '~s': ~p",
469                               [Username, VirtualHost, Error]),
470            erlang:raise(Class, Error, Stacktrace)
471    end.
472
473
474update_user(Username, Fun) ->
475    rabbit_misc:execute_mnesia_transaction(
476      rabbit_misc:with_user(
477        Username,
478        fun () ->
479                {ok, User} = lookup_user(Username),
480                ok = mnesia:write(rabbit_user, Fun(User), write)
481        end)).
482
483set_topic_permissions(Username, VirtualHost, Exchange, WritePerm, ReadPerm, ActingUser) ->
484    rabbit_log:debug("Asked to set topic permissions on exchange '~s' for "
485                     "user '~s' in virtual host '~s' to '~s', '~s'",
486                     [Exchange, Username, VirtualHost, WritePerm, ReadPerm]),
487    WritePermRegex = rabbit_data_coercion:to_binary(WritePerm),
488    ReadPermRegex = rabbit_data_coercion:to_binary(ReadPerm),
489    lists:map(
490        fun (RegexpBin) ->
491            case re:compile(RegexpBin) of
492                {ok, _}         -> ok;
493                {error, Reason} ->
494                    rabbit_log:warning("Failed to set topic permissions on exchange '~s' for "
495                                       "'~s' in virtual host '~s': regular expression '~s' is invalid",
496                                       [Exchange, Username, VirtualHost, RegexpBin]),
497                    throw({error, {invalid_regexp, RegexpBin, Reason}})
498            end
499        end, [WritePerm, ReadPerm]),
500    try
501        R = rabbit_misc:execute_mnesia_transaction(
502        rabbit_vhost:with_user_and_vhost(
503            Username, VirtualHost,
504            fun () -> ok = mnesia:write(
505                rabbit_topic_permission,
506                #topic_permission{
507                    topic_permission_key = #topic_permission_key{
508                        user_vhost = #user_vhost{
509                            username     = Username,
510                            virtual_host = VirtualHost},
511                        exchange = Exchange
512                    },
513                    permission = #permission{
514                            write = WritePermRegex,
515                            read  = ReadPermRegex
516                    }
517                },
518                write)
519            end)),
520        rabbit_log:info("Successfully set topic permissions on exchange '~s' for "
521                         "'~s' in virtual host '~s' to '~s', '~s'",
522                         [Exchange, Username, VirtualHost, WritePerm, ReadPerm]),
523        rabbit_event:notify(topic_permission_created, [
524            {user,      Username},
525            {vhost,     VirtualHost},
526            {exchange,  Exchange},
527            {write,     WritePermRegex},
528            {read,      ReadPermRegex},
529            {user_who_performed_action, ActingUser}]),
530        R
531    catch
532        throw:{error, {no_such_vhost, _}} = Error ->
533            rabbit_log:warning("Failed to set topic permissions on exchange '~s' for '~s': virtual host '~s' does not exist.",
534                               [Exchange, Username, VirtualHost]),
535            throw(Error);
536        throw:{error, {no_such_user, _}} = Error ->
537            rabbit_log:warning("Failed to set topic permissions on exchange '~s' for '~s': the user does not exist.",
538                               [Exchange, Username]),
539            throw(Error);
540        Class:Error:Stacktrace ->
541            rabbit_log:warning("Failed to set topic permissions on exchange '~s' for '~s' in virtual host '~s': ~p.",
542                               [Exchange, Username, VirtualHost, Error]),
543            erlang:raise(Class, Error, Stacktrace)
544    end .
545
546clear_topic_permissions(Username, VirtualHost, ActingUser) ->
547    rabbit_log:debug("Asked to clear topic permissions for '~s' in virtual host '~s'",
548                     [Username, VirtualHost]),
549    try
550        R = rabbit_misc:execute_mnesia_transaction(
551        rabbit_vhost:with_user_and_vhost(
552            Username, VirtualHost,
553            fun () ->
554                ListFunction = match_user_vhost_topic_permission(Username, VirtualHost),
555                List = ListFunction(),
556                lists:foreach(fun(X) ->
557                                ok = mnesia:delete_object(rabbit_topic_permission, X, write)
558                              end, List)
559            end)),
560        rabbit_log:info("Successfully cleared topic permissions for '~s' in virtual host '~s'",
561                        [Username, VirtualHost]),
562        rabbit_event:notify(topic_permission_deleted, [{user,  Username},
563            {vhost, VirtualHost},
564            {user_who_performed_action, ActingUser}]),
565        R
566    catch
567        throw:{error, {no_such_vhost, _}} = Error ->
568            rabbit_log:warning("Failed to clear topic permissions for '~s': virtual host '~s' does not exist",
569                               [Username, VirtualHost]),
570            throw(Error);
571        throw:{error, {no_such_user, _}} = Error ->
572            rabbit_log:warning("Failed to clear topic permissions for '~s': the user does not exist",
573                               [Username]),
574            throw(Error);
575        Class:Error:Stacktrace ->
576            rabbit_log:warning("Failed to clear topic permissions for '~s' in virtual host '~s': ~p",
577                               [Username, VirtualHost, Error]),
578            erlang:raise(Class, Error, Stacktrace)
579    end.
580
581clear_topic_permissions(Username, VirtualHost, Exchange, ActingUser) ->
582    rabbit_log:debug("Asked to clear topic permissions on exchange '~s' for '~s' in virtual host '~s'",
583                     [Exchange, Username, VirtualHost]),
584    try
585        R = rabbit_misc:execute_mnesia_transaction(
586        rabbit_vhost:with_user_and_vhost(
587            Username, VirtualHost,
588            fun () ->
589                ok = mnesia:delete(rabbit_topic_permission,
590                    #topic_permission_key{
591                        user_vhost = #user_vhost{
592                            username     = Username,
593                            virtual_host = VirtualHost},
594                        exchange = Exchange
595                    }, write)
596            end)),
597        rabbit_log:info("Successfully cleared topic permissions on exchange '~s' for '~s' in virtual host '~s'",
598                        [Exchange, Username, VirtualHost]),
599        rabbit_event:notify(permission_deleted, [{user,  Username},
600                                                 {vhost, VirtualHost},
601                                                 {user_who_performed_action, ActingUser}]),
602        R
603    catch
604        throw:{error, {no_such_vhost, _}} = Error ->
605            rabbit_log:warning("Failed to clear topic permissions on exchange '~s' for '~s': virtual host '~s' does not exist",
606                               [Exchange, Username, VirtualHost]),
607            throw(Error);
608        throw:{error, {no_such_user, _}} = Error ->
609            rabbit_log:warning("Failed to clear topic permissions on exchange '~s' for '~s': the user does not exist",
610                               [Exchange, Username]),
611            throw(Error);
612        Class:Error:Stacktrace ->
613            rabbit_log:warning("Failed to clear topic permissions on exchange '~s' for '~s' in virtual host '~s': ~p",
614                               [Exchange, Username, VirtualHost, Error]),
615            erlang:raise(Class, Error, Stacktrace)
616    end.
617
618put_user(User, ActingUser) -> put_user(User, undefined, ActingUser).
619
620put_user(User, Version, ActingUser) ->
621    Username        = maps:get(name, User),
622    HasPassword     = maps:is_key(password, User),
623    HasPasswordHash = maps:is_key(password_hash, User),
624    Password        = maps:get(password, User, undefined),
625    PasswordHash    = maps:get(password_hash, User, undefined),
626
627    Tags            = case {maps:get(tags, User, undefined), maps:get(administrator, User, undefined)} of
628                          {undefined, undefined} ->
629                              throw({error, tags_not_present});
630                          {undefined, AdminS} ->
631                              case rabbit_misc:parse_bool(AdminS) of
632                                  true  -> [administrator];
633                                  false -> []
634                              end;
635                          {TagsVal, _} ->
636                              tag_list_from(TagsVal)
637                      end,
638
639    %% pre-configured, only applies to newly created users
640    Permissions     = maps:get(permissions, User, undefined),
641
642    PassedCredentialValidation =
643        case {HasPassword, HasPasswordHash} of
644            {true, false} ->
645                rabbit_credential_validation:validate(Username, Password) =:= ok;
646            {false, true} -> true;
647            _             ->
648                rabbit_credential_validation:validate(Username, Password) =:= ok
649        end,
650
651    case exists(Username) of
652        true  ->
653            case {HasPassword, HasPasswordHash} of
654                {true, false} ->
655                    update_user_password(PassedCredentialValidation, Username, Password, Tags, ActingUser);
656                {false, true} ->
657                    update_user_password_hash(Username, PasswordHash, Tags, User, Version, ActingUser);
658                {true, true} ->
659                    throw({error, both_password_and_password_hash_are_provided});
660                %% clear password, update tags if needed
661                _ ->
662                    rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser),
663                    rabbit_auth_backend_internal:clear_password(Username, ActingUser)
664            end;
665        false ->
666            case {HasPassword, HasPasswordHash} of
667                {true, false}  ->
668                    create_user_with_password(PassedCredentialValidation, Username, Password, Tags, Permissions, ActingUser);
669                {false, true}  ->
670                    create_user_with_password_hash(Username, PasswordHash, Tags, User, Version, Permissions, ActingUser);
671                {true, true}   ->
672                    throw({error, both_password_and_password_hash_are_provided});
673                {false, false} ->
674                    %% this user won't be able to sign in using
675                    %% a username/password pair but can be used for x509 certificate authentication,
676                    %% with authn backends such as HTTP or LDAP and so on.
677                    create_user_with_password(PassedCredentialValidation, Username, <<"">>, Tags, Permissions, ActingUser)
678            end
679    end.
680
681update_user_password(_PassedCredentialValidation = true,  Username, Password, Tags, ActingUser) ->
682    rabbit_auth_backend_internal:change_password(Username, Password, ActingUser),
683    rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser);
684update_user_password(_PassedCredentialValidation = false, _Username, _Password, _Tags, _ActingUser) ->
685    %% we don't log here because
686    %% rabbit_auth_backend_internal will do it
687    throw({error, credential_validation_failed}).
688
689update_user_password_hash(Username, PasswordHash, Tags, User, Version, ActingUser) ->
690    %% when a hash this provided, credential validation
691    %% is not applied
692    HashingAlgorithm = hashing_algorithm(User, Version),
693
694    Hash = rabbit_misc:b64decode_or_throw(PasswordHash),
695    rabbit_auth_backend_internal:change_password_hash(
696      Username, Hash, HashingAlgorithm),
697    rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser).
698
699create_user_with_password(_PassedCredentialValidation = true,  Username, Password, Tags, undefined, ActingUser) ->
700    rabbit_auth_backend_internal:add_user(Username, Password, ActingUser),
701    rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser);
702create_user_with_password(_PassedCredentialValidation = true,  Username, Password, Tags, PreconfiguredPermissions, ActingUser) ->
703    rabbit_auth_backend_internal:add_user(Username, Password, ActingUser),
704    rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser),
705    preconfigure_permissions(Username, PreconfiguredPermissions, ActingUser);
706create_user_with_password(_PassedCredentialValidation = false, _Username, _Password, _Tags, _, _) ->
707    %% we don't log here because
708    %% rabbit_auth_backend_internal will do it
709    throw({error, credential_validation_failed}).
710
711create_user_with_password_hash(Username, PasswordHash, Tags, User, Version, PreconfiguredPermissions, ActingUser) ->
712    %% when a hash this provided, credential validation
713    %% is not applied
714    HashingAlgorithm = hashing_algorithm(User, Version),
715    Hash             = rabbit_misc:b64decode_or_throw(PasswordHash),
716
717    %% first we create a user with dummy credentials and no
718    %% validation applied, then we update password hash
719    TmpPassword = rabbit_guid:binary(rabbit_guid:gen_secure(), "tmp"),
720    rabbit_auth_backend_internal:add_user_sans_validation(Username, TmpPassword, ActingUser),
721
722    rabbit_auth_backend_internal:change_password_hash(
723      Username, Hash, HashingAlgorithm),
724    rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser),
725    preconfigure_permissions(Username, PreconfiguredPermissions, ActingUser).
726
727preconfigure_permissions(_Username, undefined, _ActingUser) ->
728    ok;
729preconfigure_permissions(Username, Map, ActingUser) when is_map(Map) ->
730    maps:map(fun(VHost, M) ->
731                     rabbit_auth_backend_internal:set_permissions(Username, VHost,
732                                                  maps:get(<<"configure">>, M),
733                                                  maps:get(<<"write">>,     M),
734                                                  maps:get(<<"read">>,      M),
735                                                  ActingUser)
736             end,
737             Map),
738    ok.
739
740set_user_limits(Username, Definition, ActingUser) when is_list(Definition); is_binary(Definition) ->
741    case rabbit_feature_flags:is_enabled(user_limits) of
742        true  ->
743            case rabbit_json:try_decode(rabbit_data_coercion:to_binary(Definition)) of
744                {ok, Term} ->
745                    validate_parameters_and_update_limit(Username, Term, ActingUser);
746                {error, Reason} ->
747                    {error_string, rabbit_misc:format(
748                                     "JSON decoding error. Reason: ~ts", [Reason])}
749            end;
750        false -> {error_string, "cannot set any user limits: the user_limits feature flag is not enabled"}
751    end;
752set_user_limits(Username, Definition, ActingUser) when is_map(Definition) ->
753    case rabbit_feature_flags:is_enabled(user_limits) of
754        true  -> validate_parameters_and_update_limit(Username, Definition, ActingUser);
755        false -> {error_string, "cannot set any user limits: the user_limits feature flag is not enabled"}
756    end.
757
758validate_parameters_and_update_limit(Username, Term, ActingUser) ->
759    case flatten_errors(rabbit_parameter_validation:proplist(
760                        <<"user-limits">>, user_limit_validation(), Term)) of
761        ok ->
762            update_user(Username, fun(User) ->
763                                      internal_user:update_limits(add, User, Term)
764                                  end),
765            notify_limit_set(Username, ActingUser, Term);
766        {errors, [{Reason, Arguments}]} ->
767            {error_string, rabbit_misc:format(Reason, Arguments)}
768    end.
769
770user_limit_validation() ->
771    [{<<"max-connections">>, fun rabbit_parameter_validation:integer/2, optional},
772     {<<"max-channels">>, fun rabbit_parameter_validation:integer/2, optional}].
773
774clear_user_limits(Username, <<"all">>, ActingUser) ->
775    update_user(Username, fun(User) ->
776                              internal_user:clear_limits(User)
777                          end),
778    notify_limit_clear(Username, ActingUser);
779clear_user_limits(Username, LimitType, ActingUser) ->
780    update_user(Username, fun(User) ->
781                              internal_user:update_limits(remove, User, LimitType)
782                          end),
783    notify_limit_clear(Username, ActingUser).
784
785tag_list_from(Tags) when is_list(Tags) ->
786    [to_atom(string:strip(to_list(T))) || T <- Tags];
787tag_list_from(Tags) when is_binary(Tags) ->
788    [to_atom(string:strip(T)) || T <- string:tokens(to_list(Tags), ",")].
789
790flatten_errors(L) ->
791    case [{F, A} || I <- lists:flatten([L]), {error, F, A} <- [I]] of
792        [] -> ok;
793        E  -> {errors, E}
794    end.
795
796%%----------------------------------------------------------------------------
797%% Listing
798
799-define(PERMS_INFO_KEYS, [configure, write, read]).
800-define(USER_INFO_KEYS, [user, tags]).
801
802-spec user_info_keys() -> rabbit_types:info_keys().
803
804user_info_keys() -> ?USER_INFO_KEYS.
805
806-spec perms_info_keys() -> rabbit_types:info_keys().
807
808perms_info_keys()            -> [user, vhost | ?PERMS_INFO_KEYS].
809
810-spec vhost_perms_info_keys() -> rabbit_types:info_keys().
811
812vhost_perms_info_keys()      -> [user | ?PERMS_INFO_KEYS].
813
814-spec user_perms_info_keys() -> rabbit_types:info_keys().
815
816user_perms_info_keys()       -> [vhost | ?PERMS_INFO_KEYS].
817
818-spec user_vhost_perms_info_keys() -> rabbit_types:info_keys().
819
820user_vhost_perms_info_keys() -> ?PERMS_INFO_KEYS.
821
822topic_perms_info_keys()            -> [user, vhost, exchange, write, read].
823user_topic_perms_info_keys()       -> [vhost, exchange, write, read].
824vhost_topic_perms_info_keys()      -> [user, exchange, write, read].
825user_vhost_topic_perms_info_keys() -> [exchange, write, read].
826
827all_users() -> mnesia:dirty_match_object(rabbit_user, internal_user:pattern_match_all()).
828
829-spec list_users() -> [rabbit_types:infos()].
830
831list_users() ->
832    [extract_internal_user_params(U) ||
833        U <- all_users()].
834
835-spec list_users(reference(), pid()) -> 'ok'.
836
837list_users(Ref, AggregatorPid) ->
838    rabbit_control_misc:emitting_map(
839      AggregatorPid, Ref,
840      fun(U) -> extract_internal_user_params(U) end,
841      all_users()).
842
843-spec list_permissions() -> [rabbit_types:infos()].
844
845list_permissions() ->
846    list_permissions(perms_info_keys(), match_user_vhost('_', '_')).
847
848list_permissions(Keys, QueryThunk) ->
849    [extract_user_permission_params(Keys, U) ||
850        U <- rabbit_misc:execute_mnesia_transaction(QueryThunk)].
851
852list_permissions(Keys, QueryThunk, Ref, AggregatorPid) ->
853    rabbit_control_misc:emitting_map(
854      AggregatorPid, Ref, fun(U) -> extract_user_permission_params(Keys, U) end,
855      rabbit_misc:execute_mnesia_transaction(QueryThunk)).
856
857filter_props(Keys, Props) -> [T || T = {K, _} <- Props, lists:member(K, Keys)].
858
859-spec list_user_permissions
860        (rabbit_types:username()) -> [rabbit_types:infos()].
861
862list_user_permissions(Username) ->
863    list_permissions(
864      user_perms_info_keys(),
865      rabbit_misc:with_user(Username, match_user_vhost(Username, '_'))).
866
867-spec list_user_permissions
868        (rabbit_types:username(), reference(), pid()) -> 'ok'.
869
870list_user_permissions(Username, Ref, AggregatorPid) ->
871    list_permissions(
872      user_perms_info_keys(),
873      rabbit_misc:with_user(Username, match_user_vhost(Username, '_')),
874      Ref, AggregatorPid).
875
876-spec list_vhost_permissions
877        (rabbit_types:vhost()) -> [rabbit_types:infos()].
878
879list_vhost_permissions(VHostPath) ->
880    list_permissions(
881      vhost_perms_info_keys(),
882      rabbit_vhost:with(VHostPath, match_user_vhost('_', VHostPath))).
883
884-spec list_vhost_permissions
885        (rabbit_types:vhost(), reference(), pid()) -> 'ok'.
886
887list_vhost_permissions(VHostPath, Ref, AggregatorPid) ->
888    list_permissions(
889      vhost_perms_info_keys(),
890      rabbit_vhost:with(VHostPath, match_user_vhost('_', VHostPath)),
891      Ref, AggregatorPid).
892
893-spec list_user_vhost_permissions
894        (rabbit_types:username(), rabbit_types:vhost()) -> [rabbit_types:infos()].
895
896list_user_vhost_permissions(Username, VHostPath) ->
897    list_permissions(
898      user_vhost_perms_info_keys(),
899      rabbit_vhost:with_user_and_vhost(
900        Username, VHostPath, match_user_vhost(Username, VHostPath))).
901
902extract_user_permission_params(Keys, #user_permission{
903                                        user_vhost =
904                                            #user_vhost{username     = Username,
905                                                        virtual_host = VHostPath},
906                                        permission = #permission{
907                                                        configure = ConfigurePerm,
908                                                        write     = WritePerm,
909                                                        read      = ReadPerm}}) ->
910    filter_props(Keys, [{user,      Username},
911                        {vhost,     VHostPath},
912                        {configure, ConfigurePerm},
913                        {write,     WritePerm},
914                        {read,      ReadPerm}]).
915
916extract_internal_user_params(User) ->
917    [{user, internal_user:get_username(User)},
918     {tags, internal_user:get_tags(User)}].
919
920match_user_vhost(Username, VHostPath) ->
921    fun () -> mnesia:match_object(
922                rabbit_user_permission,
923                #user_permission{user_vhost = #user_vhost{
924                                   username     = Username,
925                                   virtual_host = VHostPath},
926                                 permission = '_'},
927                read)
928    end.
929
930list_topic_permissions() ->
931    list_topic_permissions(topic_perms_info_keys(), match_user_vhost_topic_permission('_', '_')).
932
933list_user_topic_permissions(Username) ->
934    list_topic_permissions(user_topic_perms_info_keys(),
935        rabbit_misc:with_user(Username, match_user_vhost_topic_permission(Username, '_'))).
936
937list_vhost_topic_permissions(VHost) ->
938    list_topic_permissions(vhost_topic_perms_info_keys(),
939        rabbit_vhost:with(VHost, match_user_vhost_topic_permission('_', VHost))).
940
941list_user_vhost_topic_permissions(Username, VHost) ->
942    list_topic_permissions(user_vhost_topic_perms_info_keys(),
943        rabbit_vhost:with_user_and_vhost(Username, VHost, match_user_vhost_topic_permission(Username, VHost))).
944
945list_topic_permissions(Keys, QueryThunk) ->
946    [extract_topic_permission_params(Keys, U) ||
947        U <- rabbit_misc:execute_mnesia_transaction(QueryThunk)].
948
949match_user_vhost_topic_permission(Username, VHostPath) ->
950    match_user_vhost_topic_permission(Username, VHostPath, '_').
951
952match_user_vhost_topic_permission(Username, VHostPath, Exchange) ->
953    fun () -> mnesia:match_object(
954        rabbit_topic_permission,
955        #topic_permission{topic_permission_key = #topic_permission_key{
956            user_vhost = #user_vhost{
957                username     = Username,
958                virtual_host = VHostPath},
959            exchange = Exchange},
960            permission = '_'},
961        read)
962    end.
963
964extract_topic_permission_params(Keys, #topic_permission{
965            topic_permission_key = #topic_permission_key{
966                                    user_vhost = #user_vhost{username     = Username,
967                                                             virtual_host = VHostPath},
968                                    exchange = Exchange},
969            permission = #permission{
970                write     = WritePerm,
971                read      = ReadPerm}}) ->
972    filter_props(Keys, [{user,      Username},
973        {vhost,     VHostPath},
974        {exchange,  Exchange},
975        {write,     WritePerm},
976        {read,      ReadPerm}]).
977
978hashing_algorithm(User, Version) ->
979    case maps:get(hashing_algorithm, User, undefined) of
980        undefined ->
981            case Version of
982                %% 3.6.1 and later versions are supposed to have
983                %% the algorithm exported and thus not need a default
984                <<"3.6.0">>          -> rabbit_password_hashing_sha256;
985                <<"3.5.", _/binary>> -> rabbit_password_hashing_md5;
986                <<"3.4.", _/binary>> -> rabbit_password_hashing_md5;
987                <<"3.3.", _/binary>> -> rabbit_password_hashing_md5;
988                <<"3.2.", _/binary>> -> rabbit_password_hashing_md5;
989                <<"3.1.", _/binary>> -> rabbit_password_hashing_md5;
990                <<"3.0.", _/binary>> -> rabbit_password_hashing_md5;
991                _                    -> rabbit_password:hashing_mod()
992            end;
993        Alg       -> rabbit_data_coercion:to_atom(Alg, utf8)
994    end.
995
996is_over_connection_limit(Username) ->
997    Fun = fun() ->
998              rabbit_connection_tracking:count_tracked_items_in({user, Username})
999          end,
1000    is_over_limit(Username, <<"max-connections">>, Fun).
1001
1002is_over_channel_limit(Username) ->
1003    Fun = fun() ->
1004              rabbit_channel_tracking:count_tracked_items_in({user, Username})
1005          end,
1006    is_over_limit(Username, <<"max-channels">>, Fun).
1007
1008is_over_limit(Username, LimitType, Fun) ->
1009    case get_user_limit(Username, LimitType) of
1010        undefined -> false;
1011        {ok, 0} -> {true, 0};
1012        {ok, Limit} ->
1013            case Fun() >= Limit of
1014                false -> false;
1015                true -> {true, Limit}
1016            end
1017    end.
1018
1019get_user_limit(Username, LimitType) ->
1020    case lookup_user(Username) of
1021        {ok, User} ->
1022            case rabbit_misc:pget(LimitType, internal_user:get_limits(User)) of
1023                undefined -> undefined;
1024                N when N < 0  -> undefined;
1025                N when N >= 0 -> {ok, N}
1026            end;
1027        _ ->
1028            undefined
1029    end.
1030
1031get_user_limits() ->
1032    [{internal_user:get_username(U), internal_user:get_limits(U)} ||
1033        U <- all_users(),
1034        internal_user:get_limits(U) =/= #{}].
1035
1036get_user_limits(Username) ->
1037    case lookup_user(Username) of
1038        {ok, User} -> internal_user:get_limits(User);
1039        _ -> undefined
1040    end.
1041
1042notify_limit_set(Username, ActingUser, Term) ->
1043    rabbit_event:notify(user_limits_set,
1044        [{name, <<"limits">>},  {user_who_performed_action, ActingUser},
1045        {username, Username}  | maps:to_list(Term)]).
1046
1047notify_limit_clear(Username, ActingUser) ->
1048    rabbit_event:notify(user_limits_cleared,
1049        [{name, <<"limits">>}, {user_who_performed_action, ActingUser},
1050        {username, Username}]).
1051