ユーザーのメールアドレスを変更する

管理者がユーザーのメールアドレスを変更する際のプロセスについて、少し混乱しています。

理解できない点がいくつかあり、さらにバグも確認しました(そのため、Support ではなく bug に投稿しています)。

管理者がユーザーのプリファレンスページからそのユーザーのメールアドレスを変更した場合:

  • ユーザーにはメールアドレス変更の確認メールは送信されません。代わりに、新しいメールアドレスでアカウントのパスワードを設定できるよう、パスワードリセットメールが送信されます。
  • ユーザーには、変更されたことを知らせるメールが古いメールアドレス宛てに送信されます。

#1 なぜパスワードリセットメールが送信されるのか(「アカウントのパスワードを設定するため」)が理解できません。パスワードを変更する必要はないはずです。また、ユーザーエクスペリエンスも混乱を招きます。ユーザーはパスワードリセットメールを期待しておらず、それに伴う説明文もなく、単に「[フォーラム名] でパスワードのリセットが要求されました」と表示されるだけです。

#2 そのパスワードリセットメールが、新しいメールアドレスではなく、古いアドレス宛てに送信されてしまいます。

update_user_email の 46 行目でユーザーのメールアドレスは更新されていますが、@user オブジェクトが再読み込みされていないため、古いメールアドレスのまま残っています。

#3 操作を行ったのが管理者自身で、操作対象のユーザーがスタッフでない場合、上記の仕様通り確認メールは送信されません。にもかかわらず、メールアドレスを変更した後、管理者には以下の成功メッセージが表示されます:「そのアドレスにメールを送信しました。確認手順に従ってください」。

#4 なぜユーザーは新しいメールアドレスを確認する必要がないのでしょうか?プルリクエストは このトピック を参照していますが、多くの投稿が欠落しているように見えます。ただし、そのトピックには「通常のユーザーの場合、検証が必要なのは新しいメールアドレスだけです」という記述があります。編集:あ、#6/#7 を参照してください。

#5 管理者がユーザーのメールアドレスを変更するこのプロセスは、通常、古いメールアドレスにアクセスできなくなった場合に使われるものだと思います(推測ですが)。なぜそれでも古いアドレスに通知が送られるのでしょうか?

#6 このユーザーがログインしようとすると、ポップアップが表示されます。

まだログインできません。以前、古いメールアドレス宛てにアクティベーションメールを送信しました。アカウントを有効化するには、そのメールの手順に従ってください。

  • このようなメールは送信されていません
  • 古いメールアドレスが記載されています

「再送信」ボタンを押すと、以下が表示されます。

新しいメールアドレス宛てに別のアクティベーションメールを送信しました。到着までに数分かかる場合があります。スパムフォルダもご確認ください。

#7 そのアクティベーションメールは確かに新しいメールアドレス宛てに到着し、件名は「新しいアカウントを確認してください」(「新しいメールアドレスを確認してください」ではありません)となっています。

これは以下のようにするべきではないでしょうか?

新しいメールアドレス宛てに 1 通のメールを送信し、「[管理者名] によってメールアドレスが変更されました。以下のリンクをクリックして確認してください [リンク]」と記載する。

編集:#8 メールアドレスは、ユーザーの公開プロフィール(/u/username)から管理者が変更できますが、そのユーザーの管理者ページ(/admin/users/id/username)からは変更できません。これは直感に反します。

「いいね!」 10

tshenry さん、この現象を再現できますか?ここで回帰が発生しましたか?

「いいね!」 2

まず、私が確認している現在のフローを整理します(ほとんど、あるいはすべてが @RGJ が説明した内容と一致しているはずです):

  1. 管理者が非スタッフユーザーの設定画面に移動し、メールアドレスを変更します:

    送信後、管理者は以下のメッセージを確認します:

    そのアドレスにメールを送信しました。確認手順に従ってください。

  2. 上記のメッセージは正確ではありません。2通のメールが古いメールアドレスに送信されるためです:

    [Demo] メールアドレスが変更されました

    これは自動送信メッセージです。Demo のメールアドレスが変更されたことをお知らせします。
    誤って変更された場合は、サイト管理者までご連絡ください。

    メールアドレスが以下の通り変更されました:

    new@email.com

    [Demo] パスワードのリセット

    Demo 上でパスワードのリセットを要求されました。

    もしあなたが要求したものでない場合は、このメールを無視してください。

    新しいパスワードを設定するには、以下のリンクをクリックしてください:
    https://example.discourse.site/u/password-reset/74d53d7d2cf20dsbc360614844c653s2

  3. ここから 3 つの異なるシナリオをテストしました。各箇条書きは個別のシナリオを表します:

    • ユーザーが古いメールアドレスにアクセスでき、パスワードリセットリンクに従います。パスワードを更新するとログインできます。その後、ユーザー名または古いメールアドレスでログイン可能です。この時点では新しいメールアドレスは反映されていないようです。

    • ユーザーがユーザー名または新しいメールアドレスでログインしようとすると、以下のエラーが表示されます:

      オプション 1: 「再送」ボタンを押すと、以下のメッセージが表示されます(今回は新しいメールアドレスが記載されています):

      実際に新しいメールアドレスにメールが送信されます:

      [Demo] 新しいアカウントを確認してください

      Demo へようこそ!

      新しいアカウントを有効化するには、以下のリンクをクリックしてください:
      https://example.discourse.site/u/activate-account/74d53d7d2cf20dsbc360614844c653s2

      リンクがクリックできない場合は、コピーしてブラウザのアドレスバーに貼り付けてください。

      リンクをクリックすると、新しいアカウントの有効化に関するページがいくつか表示されます。最終的にユーザーは正常にログインできます。この時点でも、ユーザーはユーザー名または古いメールアドレスでログイン可能です。新しいメールアドレスは反映されていないようです。

      オプション 2: 「メールアドレスを変更」ボタンを押すと以下の画面が表示されます。テキストフィールドに新しいメールアドレスが入力されている場合、「メールアドレスを更新」ボタンは無効になっています。これは新しいメールアドレスがすでに有効であることを示唆していますが、実際にはそうではないようです。

    • ユーザーがパスワードリセットを開始すると、メールが新しいメールアドレスに送信され、リンクを通じてログインできます。他のシナリオと同様に、ユーザーはユーザー名または古いメールアドレスでログイン可能です。新しいメールアドレスは反映されていないようです。

確かにここで再現できています。明確に記述するのは難しいですが、OP とこの概要を合わせれば理解できるはずです。修正が必要な箇所がいくつかあることは確かです。

@martin 以前、コアのこの部分をいじっていただいていたと存じます。お時間のある際に、ご意見をいただけますでしょうか?

「いいね!」 9

なぜこれが後退したのでしょうか?:thinking:

編集:私も後退したことを確認しました。通常ユーザーのメールアドレスを編集すると、確認メールなどが古いメールアドレスに送信されます。これは以前はそうなっていませんでした。

「いいね!」 4

引用されたプルリクエストの説明を読んで、自分が犯人だと気づいた時のあの居心地の悪さ……

@tshenry@RGJ の詳しいご指摘をありがとうございます。今週中にこれを最優先事項として修正します。

「いいね!」 4

さて、これで要点が掴めました。削除されたコメントの履歴を古いトピックで確認しました。@sam からのこの内容を掘り起こしました(今思い出しました):

管理者によるメールのリセットは状況が全く異なります。実際には「管理者によるメールとパスワードのリセット」です。なぜなら、ユーザーがアカウントにアクセスできるなら、セルフサービスで全て行うことができるからです。

つまり、管理者がメールを変更しているため、パスワードリセットメールを送信すべきだというご提案でしょうか?もしユーザーが旧メールにアクセスできていれば、ログインして自分で手続きできたはずだからです。ただし、パスワードリセットメールは確認の役割も果たします。パスワードリセット手続きを完了させなければ(現在、そのメールが旧アドレスに送られているため不可能ですが)、新しいメールは「確認済み」とみなされず、そのためこのモーダルが表示されます:

パスワードリセットメールが旧アドレスに送られてしまうという問題は容易に修正可能です。これにより、少なくともリセット手続きを続行できる状態に持っていけます:

また、現在パスワードリセットメールが旧アドレスに送られているため、確認が行われると誤ったアドレスが確認され、ユーザーのメールが旧アドレスに戻ってしまいます。

メールを変更する管理者向けにメッセージを変更し、変更が完全に反映されるためには、ユーザーが新しいメール内のリンクをクリックしてパスワードを変更する必要があることを明確に伝えたいと思います(これで誤ったメールの問題も解決します)。

「いいね!」 4

待ってください。これが理解できません。

ユーザーが古いメールにアクセスできることと、Discourse アカウントのパスワード再設定が必要であることは、全く異なります。前者が後者を意味するわけではなく、これらは完全に異なる状況です。

管理者によるメール変更の多くは、ユーザーがその方法を知っていない場合や、管理者が一時的に email_editable = false の制限を解除する必要がある場合に行われます。

パスワード再設定がメール確認を兼ねていることに非常に混乱しています。個人的には、自分から要求したわけではないパスワード再設定には応じないでしょうし、それが必要な確認手順であることにも気づかないと思います(実際、必要だとも思いません。通常の確認メールで十分ではないでしょうか?)

「いいね!」 4

関連する可能性:

私のフォーラムのユーザーの一人が、今朝時点での最新バージョンの Discourse を実行している状態でパスワードのリセットを試みると、メールは届くものの、メール内のリンクをクリックするとエラーが発生します。

このエラーは複数のブラウザで発生しており、アドブロックも使用していません。

また、そのユーザーのアカウント設定ページに移動し「パスワードリセットメールの送信」をクリックしても、同じエラーメッセージが表示されます。

ボタン横に「(error)」と表示される前に、一瞬「(sending email)」と点滅しますが、実際にはメールは送信されていないようです。他のフォーラムからのメールは本日正常に送信されていることを確認済みです。

この機能は以前は正常に動作していましたが、ここ 1 週間以内に何らかの原因で機能しなくなったようです。

管理者としてログインした状態で、Web ブラウザで Discourse のエラーログを確認してください。より詳細なエラーレポートがそこにあるはずです。

エラーエントリは以下の通りです:

398 Job exception: The specified copy source is larger than the maximum allowable size for a copy source: 5368709120

aws-sdk-core-3.99.1/lib/seahorse/client/plugins/raise_response_errors.rb:15:in `call'

aws-sdk-s3-1.66.0/lib/aws-sdk-s3/plugins/sse_cpk.rb:22:in `call'

aws-sdk-s3-1.66.0/lib/aws-sdk-s3/plugins/dualstack.rb:26:in `call'

aws-sdk-s3-1.66.0/lib/aws-sdk-s3/plugins/accelerate.rb:35:in `call'

aws-sdk-core-3.99.1/lib/aws-sdk-core/plugins/jsonvalue_converter.rb:20:in `call'

aws-sdk-core-3.99.1/lib/aws-sdk-core/plugins/idempotency_token.rb:17:in `call'

aws-sdk-core-3.99.1/lib/aws-sdk-core/plugins/param_converter.rb:24:in `call'

aws-sdk-core-3.99.1/lib/aws-sdk-core/plugins/response_paging.rb:10:in `call'

aws-sdk-core-3.99.1/lib/seahorse/client/plugins/response_target.rb:23:in `call'

aws-sdk-core-3.99.1/lib/seahorse/client/request.rb:70:in `send_request'

aws-sdk-s3-1.66.0/lib/aws-sdk-s3/client.rb:1108:in `copy_object'

/var/www/discourse/lib/backup_restore/s3_backup_store.rb:61:in `block in vacate_legacy_prefix'

/var/www/discourse/lib/backup_restore/s3_backup_store.rb:60:in `each'

/var/www/discourse/lib/backup_restore/s3_backup_store.rb:60:in `vacate_legacy_prefix'

/var/www/discourse/app/jobs/onceoff/vacate_legacy_prefix_backups.rb:7:in `execute_onceoff'

/var/www/discourse/app/jobs/onceoff/onceoff.rb:25:in `execute'

/var/www/discourse/app/jobs/base.rb:232:in `block (2 levels) in perform'

rails_multisite-2.5.0/lib/rails_multisite/connection_management.rb:76:in `with_connection'

/var/www/discourse/app/jobs/base.rb:221:in `block in perform'

/var/www/discourse/app/jobs/base.rb:217:in `each'

/var/www/discourse/app/jobs/base.rb:217:in `perform'

sidekiq-6.1.2/lib/sidekiq/processor.rb:196:in `execute_job'

sidekiq-6.1.2/lib/sidekiq/processor.rb:164:in `block (2 levels) in process'

sidekiq-6.1.2/lib/sidekiq/middleware/chain.rb:138:in `block in invoke'

/var/www/discourse/lib/sidekiq/pausable.rb:138:in `call'

sidekiq-6.1.2/lib/sidekiq/middleware/chain.rb:140:in `block in invoke'

sidekiq-6.1.2/lib/sidekiq/middleware/chain.rb:143:in `invoke'

sidekiq-6.1.2/lib/sidekiq/processor.rb:163:in `block in process'

sidekiq-6.1.2/lib/sidekiq/processor.rb:136:in `block (6 levels) in dispatch'

sidekiq-6.1.2/lib/sidekiq/job_retry.rb:111:in `local'

sidekiq-6.1.2/lib/sidekiq/processor.rb:135:in `block (5 levels) in dispatch'

sidekiq-6.1.2/lib/sidekiq.rb:38:in `block in <module:Sidekiq>'

sidekiq-6.1.2/lib/sidekiq/processor.rb:131:in `block (4 levels) in dispatch'

sidekiq-6.1.2/lib/sidekiq/processor.rb:257:in `stats'

sidekiq-6.1.2/lib/sidekiq/processor.rb:126:in `block (3 levels) in dispatch'

sidekiq-6.1.2/lib/sidekiq/job_logger.rb:13:in `call'

sidekiq-6.1.2/lib/sidekiq/processor.rb:125:in `block (2 levels) in dispatch'

sidekiq-6.1.2/lib/sidekiq/job_retry.rb:78:in `global'

sidekiq-6.1.2/lib/sidekiq/processor.rb:124:in `block in dispatch'

sidekiq-6.1.2/lib/sidekiq/logger.rb:10:in `with'

sidekiq-6.1.2/lib/sidekiq/job_logger.rb:33:in `prepare'

sidekiq-6.1.2/lib/sidekiq/processor.rb:123:in `dispatch'

sidekiq-6.1.2/lib/sidekiq/processor.rb:162:in `process'

sidekiq-6.1.2/lib/sidekiq/processor.rb:78:in `process_one'

sidekiq-6.1.2/lib/sidekiq/processor.rb:68:in `run'

sidekiq-6.1.2/lib/sidekiq/util.rb:15:in `watchdog'

sidekiq-6.1.2/lib/sidekiq/util.rb:24:in `block in safe_thread'
「いいね!」 1

いいえ、それは全く無関係です。
ログをクリアしてこのエラーを発生させ、再度ログを確認してください。

その後、エラーが発生してもログは空白のままです。

おっしゃる意図は理解できます。私自身も昨日、パスワードリセットを確認手段として使う点で混乱しました。管理者がユーザーのメールを変更する際、オプションとして「ユーザーのパスワードもリセットする」というチェックボックスを追加できる仕組みがあれば良いと思います。現状ではプロセスが完全に破綻しているため、私が作成した修正用のPRはそのままマージします。

パスワードリセットフローの背景について当初説明してくれた @sam にも、新しいプロセスやフローについて意見を伺いたいです。

  1. 管理者がユーザーのメールを変更する。その際、パスワードのリセットも同時に行うかどうかを選択できる。
  2. ユーザーは新しいアドレスに、メール変更の確認を求めるメールを受け取る。
    • 「はい」の場合、メールを変更する。その後、古いアドレスに変更完了の通知メールを送信する。
    • 「いいえ」の場合、何もしない。
    1. で管理者がパスワードリセットを指定していた場合、ユーザーがメール変更を確認した瞬間、新しいアドレスにパスワードリセットメールが送信される。

このようにすれば非常に明確になり、パスワードリセットはメール変更の確認とは無関係なものになると思います。

「いいね!」 2

はい、なぜこの 2 つのことが関連する必要があるのか、私には理解できません。

「いいね!」 1

素晴らしい、1 つの新しいマージされたコミットが見つかりました。きっとみんなも喜ぶでしょう :smiley:

「いいね!」 3

ありがとうございます。これで「完全に破綻している」状態に対する修正がマージされます。別の PR も続きます!

これは以前のトピックで Sam と行った議論に基づき、このように行われました。新しいプロセスに従って、混乱のベールを払い、無関係な事柄同士のリンクを解消します。

「いいね!」 4

この PR をマージしました。以下の変更を行います:

  • ユーザーのメールアドレス変更フローを変更し、変更を確認するためのメールをユーザーに送信するようにしました
  • メール変更リクエストを誰が依頼したかを記録するようにしました
  • リクエストしたユーザーが管理者であり、かつ対象のユーザーではない場合、ユーザーに送信されるメールにその旨を記載します
  • メール変更確認用のルートは匿名ユーザーにも開放するようにしました。これにより、アカウントにアクセスできないユーザーでもリンクをクリックして確認できます。ログイン中のユーザーがいる場合は、確認が現在のユーザーと一致することを確認します。

これでプロセスがより明確になることを願っています!

「いいね!」 4