diff options
author | dcorbacho <dparracorbacho@piotal.io> | 2021-12-10 16:23:06 +0100 |
---|---|---|
committer | dcorbacho <dparracorbacho@piotal.io> | 2021-12-14 11:05:20 +0100 |
commit | 4d4864284814947495b10ee2cb435fb547b12848 (patch) | |
tree | d90022b74e5a81819c53c6fe2fa6c1f48cb71553 | |
parent | 0e5eb5f63f246943283cd18c12166f929a2d8153 (diff) | |
download | rabbitmq-server-git-khepri-import-optimizations.tar.gz |
Optimise user declare/update/import by reducing the number of operationskhepri-import-optimizations
User, tags and hashed password can be inserted/updated at once,
increasing the performance of the storage backend - both for mnesia
and khepri.
-rw-r--r-- | deps/rabbit/src/rabbit_auth_backend_internal.erl | 124 |
1 files changed, 99 insertions, 25 deletions
diff --git a/deps/rabbit/src/rabbit_auth_backend_internal.erl b/deps/rabbit/src/rabbit_auth_backend_internal.erl index f435f0fb75..44ec6b27f3 100644 --- a/deps/rabbit/src/rabbit_auth_backend_internal.erl +++ b/deps/rabbit/src/rabbit_auth_backend_internal.erl @@ -23,12 +23,15 @@ check_vhost_access/3, check_resource_access/4, check_topic_access/4, with_user/2, with_user_in_mnesia/2, with_user_in_khepri/2]). --export([add_user/3, delete_user/2, lookup_user/1, exists/1, +-export([add_user/3, add_user/4, delete_user/2, lookup_user/1, exists/1, change_password/3, clear_password/2, hash_password/2, change_password_hash/2, change_password_hash/3, set_tags/3, set_permissions/6, clear_permissions/3, set_topic_permissions/6, clear_topic_permissions/3, clear_topic_permissions/4, - add_user_sans_validation/3, put_user/2, put_user/3]). + add_user_sans_validation/3, put_user/2, put_user/3, + change_password_and_tags/4, + change_password_hash_and_tags/4, + add_user_sans_validation/5]). -export([set_user_limits/3, clear_user_limits/3, is_over_connection_limit/1, is_over_channel_limit/1, get_user_limits/0, get_user_limits/1]). @@ -341,15 +344,45 @@ add_user(Username, Password, ActingUser) -> validate_and_alternate_credentials(Username, Password, ActingUser, fun add_user_sans_validation/3). +-spec add_user(rabbit_types:username(), rabbit_types:password(), + rabbit_types:username(), [atom()]) -> 'ok' | {'error', string()}. + +add_user(Username, Password, ActingUser, Tags) -> + validate_and_alternate_credentials(Username, Password, ActingUser, + add_user_sans_validation(Tags)). + add_user_sans_validation(Username, Password, ActingUser) -> + add_user_sans_validation(Username, Password, ActingUser, []). + +add_user_sans_validation(Tags) -> + fun(Username, Password, ActingUser) -> + add_user_sans_validation(Username, Password, ActingUser, Tags) + end. + +add_user_sans_validation(Username, Password, ActingUser, Tags) -> rabbit_log:debug("Asked to create a new user '~s', password length in bytes: ~p", [Username, bit_size(Password)]), %% hash_password will pick the hashing function configured for us %% but we also need to store a hint as part of the record, so we %% retrieve it here one more time HashingMod = rabbit_password:hashing_mod(), PasswordHash = hash_password(HashingMod, Password), - User = internal_user:create_user(Username, PasswordHash, HashingMod), + User0 = internal_user:create_user(Username, PasswordHash, HashingMod), + ConvertedTags = [rabbit_data_coercion:to_atom(I) || I <- Tags], + User = internal_user:set_tags(User0, ConvertedTags), + add_user_sans_validation_in(Username, User, ConvertedTags, ActingUser). +add_user_sans_validation(Username, PasswordHash, HashingAlgorithm, Tags, ActingUser) -> + rabbit_log:debug("Asked to create a new user '~s' with password hash", [Username]), + ConvertedTags = [rabbit_data_coercion:to_atom(I) || I <- Tags], + HashingMod = rabbit_password:hashing_mod(), + User0 = internal_user:create_user(Username, PasswordHash, HashingMod), + User = internal_user:set_tags( + internal_user:set_password_hash(User0, + PasswordHash, HashingAlgorithm), + ConvertedTags), + add_user_sans_validation_in(Username, User, ConvertedTags, ActingUser). + +add_user_sans_validation_in(Username, User, ConvertedTags, ActingUser) -> try R = rabbit_khepri:try_mnesia_or_khepri( fun() -> add_user_sans_validation_in_mnesia(Username, User) end, @@ -357,6 +390,10 @@ add_user_sans_validation(Username, Password, ActingUser) -> rabbit_log:info("Created user '~s'", [Username]), rabbit_event:notify(user_created, [{name, Username}, {user_who_performed_action, ActingUser}]), + case ConvertedTags of + [] -> ok; + _ -> notify_user_tags_set(Username, ConvertedTags, ActingUser) + end, R catch throw:{error, {user_already_exists, _}} = Error -> @@ -502,6 +539,41 @@ change_password_sans_validation(Username, Password, ActingUser) -> erlang:raise(Class, Error, Stacktrace) end. +change_password_and_tags(Username, Password, Tags, ActingUser) -> + validate_and_alternate_credentials(Username, Password, ActingUser, + change_password_and_tags_sans_validation(Tags)). + +change_password_and_tags_sans_validation(Tags) -> + fun(Username, Password, ActingUser) -> + try + rabbit_log:debug("Asked to change password of user '~s', new password length in bytes: ~p", [Username, bit_size(Password)]), + HashingAlgorithm = rabbit_password:hashing_mod(), + + rabbit_log:debug("Asked to set user tags for user '~s' to ~p", [Username, Tags]), + + ConvertedTags = [rabbit_data_coercion:to_atom(I) || I <- Tags], + R = change_password_hash_and_tags(Username, + hash_password(rabbit_password:hashing_mod(), + Password), + HashingAlgorithm, + ConvertedTags), + rabbit_log:info("Successfully changed password for user '~s'", [Username]), + rabbit_event:notify(user_password_changed, + [{name, Username}, + {user_who_performed_action, ActingUser}]), + + notify_user_tags_set(Username, ConvertedTags, ActingUser), + R + catch + throw:{error, {no_such_user, _}} = Error -> + rabbit_log:warning("Failed to change password for user '~s': the user does not exist", [Username]), + throw(Error); + Class:Error:Stacktrace -> + rabbit_log:warning("Failed to change password for user '~s': ~p", [Username, Error]), + erlang:raise(Class, Error, Stacktrace) + end + end. + -spec clear_password(rabbit_types:username(), rabbit_types:username()) -> 'ok'. clear_password(Username, ActingUser) -> @@ -531,6 +603,14 @@ change_password_hash(Username, PasswordHash, HashingAlgorithm) -> PasswordHash, HashingAlgorithm) end). +change_password_hash_and_tags(Username, PasswordHash, HashingAlgorithm, ConvertedTags) -> + update_user(Username, fun(User) -> + internal_user:set_tags( + internal_user:set_password_hash(User, + PasswordHash, HashingAlgorithm), + ConvertedTags) + end). + -spec set_tags(rabbit_types:username(), [atom()], rabbit_types:username()) -> 'ok'. set_tags(Username, Tags, ActingUser) -> @@ -540,9 +620,7 @@ set_tags(Username, Tags, ActingUser) -> R = update_user(Username, fun(User) -> internal_user:set_tags(User, ConvertedTags) end), - rabbit_log:info("Successfully set user tags for user '~s' to ~p", [Username, ConvertedTags]), - rabbit_event:notify(user_tags_set, [{name, Username}, {tags, ConvertedTags}, - {user_who_performed_action, ActingUser}]), + notify_user_tags_set(Username, ConvertedTags, ActingUser), R catch throw:{error, {no_such_user, _}} = Error -> @@ -553,6 +631,11 @@ set_tags(Username, Tags, ActingUser) -> erlang:raise(Class, Error, Stacktrace) end . +notify_user_tags_set(Username, ConvertedTags, ActingUser) -> + rabbit_log:info("Successfully set user tags for user '~s' to ~p", [Username, ConvertedTags]), + rabbit_event:notify(user_tags_set, [{name, Username}, {tags, ConvertedTags}, + {user_who_performed_action, ActingUser}]). + -spec set_permissions (rabbit_types:username(), rabbit_types:vhost(), regexp(), regexp(), regexp(), rabbit_types:username()) -> @@ -993,7 +1076,7 @@ put_user(User, Version, ActingUser) -> {true, false} -> update_user_password(PassedCredentialValidation, Username, Password, Tags, ActingUser); {false, true} -> - update_user_password_hash(Username, PasswordHash, Tags, User, Version, ActingUser); + update_user_password_hash(Username, PasswordHash, Tags, User, Version); {true, true} -> throw({error, both_password_and_password_hash_are_provided}); %% clear password, update tags if needed @@ -1018,29 +1101,27 @@ put_user(User, Version, ActingUser) -> end. update_user_password(_PassedCredentialValidation = true, Username, Password, Tags, ActingUser) -> - rabbit_auth_backend_internal:change_password(Username, Password, ActingUser), - rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser); + %% change_password, set_tags + rabbit_auth_backend_internal:change_password_and_tags(Username, Password, Tags, ActingUser); update_user_password(_PassedCredentialValidation = false, _Username, _Password, _Tags, _ActingUser) -> %% we don't log here because %% rabbit_auth_backend_internal will do it throw({error, credential_validation_failed}). -update_user_password_hash(Username, PasswordHash, Tags, User, Version, ActingUser) -> +update_user_password_hash(Username, PasswordHash, Tags, User, Version) -> %% when a hash this provided, credential validation %% is not applied HashingAlgorithm = hashing_algorithm(User, Version), Hash = rabbit_misc:b64decode_or_throw(PasswordHash), - rabbit_auth_backend_internal:change_password_hash( - Username, Hash, HashingAlgorithm), - rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser). + ConvertedTags = [rabbit_data_coercion:to_atom(I) || I <- Tags], + rabbit_auth_backend_internal:change_password_hash_and_tags( + Username, Hash, HashingAlgorithm, ConvertedTags). create_user_with_password(_PassedCredentialValidation = true, Username, Password, Tags, undefined, ActingUser) -> - rabbit_auth_backend_internal:add_user(Username, Password, ActingUser), - rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser); + rabbit_auth_backend_internal:add_user(Username, Password, ActingUser, Tags); create_user_with_password(_PassedCredentialValidation = true, Username, Password, Tags, PreconfiguredPermissions, ActingUser) -> - rabbit_auth_backend_internal:add_user(Username, Password, ActingUser), - rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser), + rabbit_auth_backend_internal:add_user(Username, Password, ActingUser, Tags), preconfigure_permissions(Username, PreconfiguredPermissions, ActingUser); create_user_with_password(_PassedCredentialValidation = false, _Username, _Password, _Tags, _, _) -> %% we don't log here because @@ -1053,14 +1134,7 @@ create_user_with_password_hash(Username, PasswordHash, Tags, User, Version, Prec HashingAlgorithm = hashing_algorithm(User, Version), Hash = rabbit_misc:b64decode_or_throw(PasswordHash), - %% first we create a user with dummy credentials and no - %% validation applied, then we update password hash - TmpPassword = rabbit_guid:binary(rabbit_guid:gen_secure(), "tmp"), - rabbit_auth_backend_internal:add_user_sans_validation(Username, TmpPassword, ActingUser), - - rabbit_auth_backend_internal:change_password_hash( - Username, Hash, HashingAlgorithm), - rabbit_auth_backend_internal:set_tags(Username, Tags, ActingUser), + rabbit_auth_backend_internal:add_user_sans_validation(Username, Hash, HashingAlgorithm, Tags, ActingUser), preconfigure_permissions(Username, PreconfiguredPermissions, ActingUser). preconfigure_permissions(_Username, undefined, _ActingUser) -> |