@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 PhabricatorAuthFactorProviderStatusTransaction
4 extends PhabricatorAuthFactorProviderTransactionType {
5
6 const TRANSACTIONTYPE = 'status';
7
8 public function generateOldValue($object) {
9 return $object->getStatus();
10 }
11
12 public function applyInternalEffects($object, $value) {
13 $object->setStatus($value);
14 }
15
16 public function getTitle() {
17 $old = $this->getOldValue();
18 $new = $this->getNewValue();
19
20 $old_display = PhabricatorAuthFactorProviderStatus::newForStatus($old)
21 ->getName();
22 $new_display = PhabricatorAuthFactorProviderStatus::newForStatus($new)
23 ->getName();
24
25 return pht(
26 '%s changed the status of this provider from %s to %s.',
27 $this->renderAuthor(),
28 $this->renderValue($old_display),
29 $this->renderValue($new_display));
30 }
31
32 public function validateTransactions($object, array $xactions) {
33 $errors = array();
34 $actor = $this->getActor();
35
36 $map = PhabricatorAuthFactorProviderStatus::getMap();
37 foreach ($xactions as $xaction) {
38 $new_value = $xaction->getNewValue();
39
40 if (!isset($map[$new_value])) {
41 $errors[] = $this->newInvalidError(
42 pht(
43 'Status "%s" is invalid. Valid statuses are: %s.',
44 $new_value,
45 implode(', ', array_keys($map))),
46 $xaction);
47 continue;
48 }
49
50 $require_key = 'security.require-multi-factor-auth';
51 $require_mfa = PhabricatorEnv::getEnvConfig($require_key);
52
53 if ($require_mfa) {
54 $status_active = PhabricatorAuthFactorProviderStatus::STATUS_ACTIVE;
55 if ($new_value !== $status_active) {
56 $active_providers = id(new PhabricatorAuthFactorProviderQuery())
57 ->setViewer($actor)
58 ->withStatuses(
59 array(
60 $status_active,
61 ))
62 ->execute();
63 $active_providers = mpull($active_providers, null, 'getID');
64 unset($active_providers[$object->getID()]);
65
66 if (!$active_providers) {
67 $errors[] = $this->newInvalidError(
68 pht(
69 'You can not deprecate or disable the last active MFA '.
70 'provider while "%s" is enabled, because new users would '.
71 'be unable to enroll in MFA. Disable the MFA requirement '.
72 'in Config, or create or enable another MFA provider first.',
73 $require_key));
74 continue;
75 }
76 }
77 }
78 }
79
80 return $errors;
81 }
82
83 public function didCommitTransaction($object, $value) {
84 $status = PhabricatorAuthFactorProviderStatus::newForStatus($value);
85
86 // If a provider has undergone a status change, reset the MFA enrollment
87 // cache for all users. This may immediately force a lot of users to redo
88 // MFA enrollment.
89
90 // We could be more surgical about this: we only really need to affect
91 // users who had a factor under the provider, and only really need to
92 // do anything if a provider was disabled. This is just a little simpler.
93
94 $table = new PhabricatorUser();
95 $conn = $table->establishConnection('w');
96
97 queryfx(
98 $conn,
99 'UPDATE %R SET isEnrolledInMultiFactor = 0',
100 $table);
101 }
102
103}