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