ユーザーのなりすまし
一部のバグは一人だけに存在します。開かないレコード、存在しないメニュー項目、見た目の合算数など、すべては その 連絡先に紐づくウェブロール、テーブル権限、Dataverseセキュリティロールに依存します。 ユーザーなりすま しは、内部管理者がその連絡先としてサインインし、見たものを正確に見てから退出できるようにします。
助けになるとき
なりすましは問題の説明では答えられない疑問に答えます。
- サポートの問題を再現すること。 どの許可が欠けているかを考えるのではなく、報告者の視点でページを見ているのです。
- 権限変更の確認。 ウェブロールやテーブルの権限を編集した後は、実際の連絡先への影響を推測するのではなく確認してください。
- 役割が実際に何を露出しているかを確認すること。 本番前、意図されたアクセスと実際のアクセスのギャップが間違えやすい時期に有用です。
使い方
このアフォーダンスはプロファイルメニューに存在し、使用許可されたユーザーにのみ表示されます。
- 管理者の役割を持つ内部ユーザーとしてサインインし、プロファイルメニューを開き「 別のユーザーとして見る」を選択してください。
- 名前やメールアドレスで検索するには最低3文字を入力し、完全に一致するユーザーIDを貼り付けてください。
- その人物を選んで確認してください。ポータルはその人としてリロードされます — 同じウェブロール、同じテーブル権限、同じ行です。
- バナーはその間、各ページの上部にピン留めされたまま残ります。自分のセッションに戻るには 「停止」 を選択してください。
誰がなりすませるか
インターフェースが提供する条件とは独立して、サーバー上で各通話ごとに2つの条件がチェックされます。
- サインインしたユーザーが
SystemAdminロールを保持します。プロジェクトがどのDataverseセキュリティロールに付与するかを決定します。生成されたテンプレートは組み込みの システム管理者 ロールと、PortalIdentityOptions.SystemAdminRoleNameまでの設定可能な役割をマッピングします。 - サインインしているユーザーは、正式なDataverse内部ユーザーです。これは役割とは別にチェックされるため、たとえプロジェクトが誤って役割を与えてもポータルの連絡先がなりすましすることはできません。
ターゲットは常にポータルの連絡先であり、連絡先テーブルだけで解決されます。他の内部ユーザーのIDを入力しても解決されません。ここから2つ目のスタッフアカウントへの経路はありません。
なりすましはアクセスを減らすだけです
内部ユーザーは連絡先となり、逆に、また他の内部ユーザーにはなりません。この方向性こそが、機能を安全に提供できる理由です。なりすましたセッションは管理者よりも限られたことができるため、使用によって得られる特権はありません。
なりすましできる人物の制限
デフォルトでは、すべてのアクティブな連絡先は候補者です。スタッフアカウントや管理者の事業部門外の記録、組織がタブリストとみなすその他の連絡先を除外する IImpersonationTargetFilter を実装しましょう。
public sealed class StaffContactsAreOffLimits : IImpersonationTargetFilter
{
public async Task<bool> CanImpersonateAsync(
ClaimsPrincipal impersonator, Guid targetContactId, CancellationToken ct)
{
// Return false to hide the contact from search AND refuse impersonation.
return await this.IsOrdinaryPortalContactAsync(targetContactId, ct);
}
}
// Program.cs
builder.Services.AddScoped<IImpersonationTargetFilter, StaffContactsAreOffLimits>();
フィルターは 検索となりすましの両方を管理します。拒否した連絡先は選択不可ではなく、ピッカーでは見えません。そうでなければ、検索は管理者が決してなれない人物を列挙する手段になってしまいます。
監査の跡
すべてのスタート、停止、拒否の試みが記録されます。拒否は意図的に記録されます。成功の連続だけでは誰かが試みたかどうかはわかりません。
Dataverseはセッションの背後に誰がいたかを記録できません
ポータルは単一のアプリケーションユーザーとしてDataverseに接続し、ターゲットをなりすましてサインインセッションを表現します。管理者は送信されないため、Dataverse監査ログでは「連絡先として行動する管理者」と「通常サインインしている連絡先」は区別できず、なりすまし中に書かれた内容は
createdbyおよびmodifiedbyで連絡先に帰属されます。ポータル側のトレイルだけがこの情報が存在するため、任意ではありません。
すべてのイベントは、箱から出してアプリケーションログに書き込まれます。監査記録を組織が保管している場所にその記録を永続化する IImpersonationAuditSink を実装しましょう。シンクは組み込みのシンクに加えて追加され、置き換えるのではありません。
public sealed class ImpersonationAuditTable : IImpersonationAuditSink
{
public Task OnStartedAsync(ImpersonationAuditEvent e, CancellationToken ct) => this.WriteAsync("started", e, ct);
public Task OnStoppedAsync(ImpersonationAuditEvent e, CancellationToken ct) => this.WriteAsync("stopped", e, ct);
// Denied attempts matter as much as successful ones — a trail of
// successes alone can't answer "did anyone try?".
public Task OnDeniedAsync(ImpersonationAuditEvent e, string reason, CancellationToken ct) => this.WriteAsync(reason, e, ct);
}
// Program.cs — added, not replaced: the framework's own logging sink stays.
builder.Services.AddTransient<IImpersonationAuditSink, ImpersonationAuditTable>();
Reactより
Reactにも同様の体験が提供されます:プロフィールメニューエントリ、ピッカー、バナー。もし自分でインターフェースを構築したいなら、基礎となるアクションは useAuth() に設定できます。
const auth = useAuth();
// Search, then start. Both are refused server-side unless the caller
// is an internal user holding the SystemAdmin role.
const { results } = await auth.searchImpersonationTargets('smith');
await auth.impersonate(results[0].contactId);
// While impersonating, `auth.user` describes the CONTACT.
auth.user.isImpersonating; // true
auth.user.impersonatorName; // the administrator behind the session
await auth.stopImpersonation();
知っておく価値がある
- なりすましの連絡先がセッションの途中でパスワードを変更すると、セッションは終了し、あなたは自分のアカウントではなくサインアウト状態に戻ります。再度サインインするとパスワードが回復します。
- なりすましは入れ子にできません。次のセッションを始める前に現在のセッションを停止してください。
- ブラウザのセッションより長くは続かず、ブラウザを閉じるとセッションが終了します。
