@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorAuthUnlinkController
4 extends PhabricatorAuthController {
5
6 public function handleRequest(AphrontRequest $request) {
7 $viewer = $this->getViewer();
8 $id = $request->getURIData('id');
9
10 $account = id(new PhabricatorExternalAccountQuery())
11 ->setViewer($viewer)
12 ->withIDs(array($id))
13 ->requireCapabilities(
14 array(
15 PhabricatorPolicyCapability::CAN_VIEW,
16 PhabricatorPolicyCapability::CAN_EDIT,
17 ))
18 ->executeOne();
19 if (!$account) {
20 return new Aphront404Response();
21 }
22
23 $done_uri = '/settings/panel/external/';
24
25 $config = $account->getProviderConfig();
26 $provider = $config->getProvider();
27 if (!$provider->shouldAllowAccountUnlink()) {
28 return $this->renderNotUnlinkableErrorDialog($provider, $done_uri);
29 }
30
31 $confirmations = $request->getStrList('confirmations');
32 $confirmations = array_fuse($confirmations);
33
34 if (!$request->isFormOrHisecPost() || !isset($confirmations['unlink'])) {
35 return $this->renderConfirmDialog($confirmations, $config, $done_uri);
36 }
37
38 // Check that this account isn't the only account which can be used to
39 // login. We warn you when you remove your only login account.
40 if ($account->isUsableForLogin()) {
41 $other_accounts = id(new PhabricatorExternalAccountQuery())
42 ->setViewer($viewer)
43 ->withUserPHIDs(array($viewer->getPHID()))
44 ->execute();
45
46 $valid_accounts = 0;
47 foreach ($other_accounts as $other_account) {
48 if ($other_account->isUsableForLogin()) {
49 $valid_accounts++;
50 }
51 }
52
53 if ($valid_accounts < 2) {
54 if (!isset($confirmations['only'])) {
55 return $this->renderOnlyUsableAccountConfirmDialog(
56 $confirmations,
57 $done_uri);
58 }
59 }
60 }
61
62 $workflow_key = sprintf(
63 'account.unlink(%s)',
64 $account->getPHID());
65
66 $hisec_token = id(new PhabricatorAuthSessionEngine())
67 ->setWorkflowKey($workflow_key)
68 ->requireHighSecurityToken($viewer, $request, $done_uri);
69
70 $account->unlinkAccount();
71
72 id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
73 $viewer,
74 new PhutilOpaqueEnvelope(
75 $request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
76
77 return id(new AphrontRedirectResponse())->setURI($done_uri);
78 }
79
80 private function renderNotUnlinkableErrorDialog(
81 PhabricatorAuthProvider $provider,
82 $done_uri) {
83
84 return $this->newDialog()
85 ->setTitle(pht('Permanent Account Link'))
86 ->appendChild(
87 pht(
88 'You can not unlink this account because the administrator has '.
89 'configured this server to make links to "%s" accounts permanent.',
90 $provider->getProviderName()))
91 ->addCancelButton($done_uri);
92 }
93
94 private function renderOnlyUsableAccountConfirmDialog(
95 array $confirmations,
96 $done_uri) {
97
98 $confirmations[] = 'only';
99
100 return $this->newDialog()
101 ->setTitle(pht('Unlink Your Only Login Account?'))
102 ->addHiddenInput('confirmations', implode(',', $confirmations))
103 ->appendParagraph(
104 pht(
105 'This is the only external login account linked to your '.
106 'account. If you remove it, you may no longer be able to log in.'))
107 ->appendParagraph(
108 pht(
109 'If you lose access to your account, you can recover access by '.
110 'sending yourself an email login link from the login screen.'))
111 ->addCancelButton($done_uri)
112 ->addSubmitButton(pht('Unlink External Account'));
113 }
114
115 private function renderConfirmDialog(
116 array $confirmations,
117 PhabricatorAuthProviderConfig $config,
118 $done_uri) {
119
120 $confirmations[] = 'unlink';
121 $provider = $config->getProvider();
122
123 $title = pht('Unlink "%s" Account?', $provider->getProviderName());
124 $body = pht(
125 'You will no longer be able to use your %s account to '.
126 'log in.',
127 $provider->getProviderName());
128
129 return $this->newDialog()
130 ->setTitle($title)
131 ->addHiddenInput('confirmations', implode(',', $confirmations))
132 ->appendParagraph($body)
133 ->appendParagraph(
134 pht(
135 'Note: Unlinking an authentication provider will terminate any '.
136 'other active login sessions.'))
137 ->addSubmitButton(pht('Unlink Account'))
138 ->addCancelButton($done_uri);
139 }
140
141}