nickva commented on code in PR #4814: URL: https://github.com/apache/couchdb/pull/4814#discussion_r1378379078
########## src/couch/src/couch_password_hasher.erl: ########## @@ -45,29 +66,95 @@ start_link() -> init(_Args) -> hash_admin_passwords(true), - {ok, #state{}}. + ?IN_PROGRESS_ETS = ets:new(?IN_PROGRESS_ETS, [named_table, {read_concurrency, true}]), + {ok, start_worker_loop(#state{})}. handle_call(Msg, _From, #state{} = State) -> {stop, {invalid_call, Msg}, {invalid_call, Msg}, State}. -handle_cast({hash_passwords, Persist}, State) -> - hash_admin_passwords(Persist), +handle_cast({upgrade_password_hash, AuthModule, UserName, Password, UserProps}, State) -> + case ets:insert_new(?IN_PROGRESS_ETS, {{AuthModule, UserName}}) of + true -> + State#state.worker_pid ! + {upgrade_password_hash, AuthModule, UserName, Password, UserProps}; + false -> + ok + end, + {noreply, State}; +handle_cast({hash_admin_passwords, Persist}, State) -> + hash_admin_passwords_int(Persist), {noreply, State}; handle_cast(Msg, State) -> {stop, {invalid_cast, Msg}, State}. +handle_info({done, AuthModule, UserName}, State) -> + ets:delete(?IN_PROGRESS_ETS, {AuthModule, UserName}), + {noreply, State}; +handle_info({'DOWN', MonRef, _, _, normal}, #state{worker_mon = MonRef} = State) -> + ets:delete_all_objects(?IN_PROGRESS_ETS), + {noreply, start_worker_loop(State)}; +handle_info(Msg, State) -> + {stop, {invalid_info, Msg}, State}. + code_change(_OldVsn, #state{} = State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -hash_admin_passwords(Persist) -> +hash_admin_passwords_int(Persist) -> lists:foreach( fun({User, ClearPassword}) -> HashedPassword = couch_passwords:hash_admin_password(ClearPassword), config:set("admins", User, ?b2l(HashedPassword), Persist) end, couch_passwords:get_unhashed_admins() ). + +is_admin(UserProps) -> + Roles = couch_util:get_value(<<"roles">>, UserProps, []), + lists:member(<<"_admin">>, Roles). + +needs_upgrade(UserProps) -> + CurrentScheme = couch_util:get_value(<<"password_scheme">>, UserProps, <<"simple">>), + TargetScheme = ?l2b(chttpd_util:get_chttpd_auth_config("password_scheme", "pbkdf2")), + CurrentPRF = couch_util:get_value(<<"pbkdf2_prf">>, UserProps, <<"sha">>), + TargetPRF = ?l2b(chttpd_util:get_chttpd_auth_config("pbkdf2_prf", "sha256")), + CurrentIterations = couch_util:to_integer( + couch_util:get_value(<<"iterations">>, UserProps, "10") + ), + TargetIterations = chttpd_util:get_chttpd_auth_config_integer( + "iterations", 10 + ), + if + CurrentScheme == TargetScheme andalso TargetScheme == <<"simple">> andalso + CurrentIterations == TargetIterations -> + false; + CurrentScheme == TargetScheme andalso TargetScheme == <<"pbkdf2">> andalso + CurrentPRF == TargetPRF andalso CurrentIterations == TargetIterations -> + false; + true -> + true + end. + +in_progress(AuthModule, UserName) -> + ets:member(?IN_PROGRESS_ETS, {AuthModule, UserName}). + +start_worker_loop(State) -> + {WorkerPid, WorkerMon} = spawn_monitor(?MODULE, worker_loop, [self()]), Review Comment: `spawn_monitor` will keep the worker up even after its parent crashes. Should we `spawn_link` it instead and then in a `terminate/2` function to `unlink` and `kill` it? If we expect it to crash often we could set the parent to trap exits and then react to an `{'EXIT', ...}` to log an error, cleanup the table and restart it again. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@couchdb.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org