Skip to content

Commit d6d3ad6

Browse files
author
epriestley
committedMar 16, 2017
Allow administrators to get a list of users who don't have MFA configured
Summary: Fixes T12400. Adds a "Has MFA" filter to People so you can figure out who you need to harass before turning on "require MFA". When you run this as a non-admin, you don't currently actually hit the exception: the query just doesn't work. I think this is probably okay, but if we add more of these it might be better to make the "this didn't work" more explicit since it could be confusing in some weird edge cases (like, an administrator sending a non-administrator a link which they expect will show the non-administrator some interesting query results, but they actually just get no constraint). The exception is more of a fail-safe in case we make application changes in the future and don't remember this weird special case. Test Plan: - As an administrator and non-administrator, used People and Conduit to query MFA, no-MFA, and don't-care-about-MFA. These queries worked for an admin and didn't work for a non-admin. - Viewed the list as an administrator, saw MFA users annotated. - Viewed config help, clicked link as an admin, ended up in the right place. {F4093033} {F4093034} Reviewers: chad Reviewed By: chad Maniphest Tasks: T12400 Differential Revision: https://secure.phabricator.com/D17500
1 parent fd69dfa commit d6d3ad6

6 files changed

+85
-18
lines changed
 

‎src/__phutil_library_map__.php

+2
Original file line numberDiff line numberDiff line change
@@ -3763,6 +3763,7 @@
37633763
'PhabricatorSearchBaseController' => 'applications/search/controller/PhabricatorSearchBaseController.php',
37643764
'PhabricatorSearchCheckboxesField' => 'applications/search/field/PhabricatorSearchCheckboxesField.php',
37653765
'PhabricatorSearchConfigOptions' => 'applications/search/config/PhabricatorSearchConfigOptions.php',
3766+
'PhabricatorSearchConstraintException' => 'applications/search/exception/PhabricatorSearchConstraintException.php',
37663767
'PhabricatorSearchController' => 'applications/search/controller/PhabricatorSearchController.php',
37673768
'PhabricatorSearchCustomFieldProxyField' => 'applications/search/field/PhabricatorSearchCustomFieldProxyField.php',
37683769
'PhabricatorSearchDAO' => 'applications/search/storage/PhabricatorSearchDAO.php',
@@ -9072,6 +9073,7 @@
90729073
'PhabricatorSearchBaseController' => 'PhabricatorController',
90739074
'PhabricatorSearchCheckboxesField' => 'PhabricatorSearchField',
90749075
'PhabricatorSearchConfigOptions' => 'PhabricatorApplicationConfigOptions',
9076+
'PhabricatorSearchConstraintException' => 'Exception',
90759077
'PhabricatorSearchController' => 'PhabricatorSearchBaseController',
90769078
'PhabricatorSearchCustomFieldProxyField' => 'PhabricatorSearchField',
90779079
'PhabricatorSearchDAO' => 'PhabricatorLiskDAO',

‎src/applications/config/option/PhabricatorSecurityConfigOptions.php

+16-7
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,21 @@ public function getOptions() {
6666
,
6767
PhabricatorEnv::getDoclink('Configuring Encryption')));
6868

69+
$require_mfa_description = $this->deformat(pht(<<<EOTEXT
70+
By default, Phabricator allows users to add multi-factor authentication to
71+
their accounts, but does not require it. By enabling this option, you can
72+
force all users to add at least one authentication factor before they can use
73+
their accounts.
74+
75+
Administrators can query a list of users who do not have MFA configured in
76+
{nav People}:
77+
78+
- **[[ %s | %s ]]**
79+
EOTEXT
80+
,
81+
'/people/?mfa=false',
82+
pht('List of Users Without MFA')));
83+
6984
return array(
7085
$this->newOption('security.alternate-file-domain', 'string', null)
7186
->setLocked(true)
@@ -132,13 +147,7 @@ public function getOptions() {
132147
->setLocked(true)
133148
->setSummary(
134149
pht('Require all users to configure multi-factor authentication.'))
135-
->setDescription(
136-
pht(
137-
'By default, Phabricator allows users to add multi-factor '.
138-
'authentication to their accounts, but does not require it. '.
139-
'By enabling this option, you can force all users to add '.
140-
'at least one authentication factor before they can use their '.
141-
'accounts.'))
150+
->setDescription($require_mfa_description)
142151
->setBoolOptions(
143152
array(
144153
pht('Multi-Factor Required'),

‎src/applications/people/query/PhabricatorPeopleQuery.php

+13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ final class PhabricatorPeopleQuery
1818
private $nameLike;
1919
private $nameTokens;
2020
private $namePrefixes;
21+
private $isEnrolledInMultiFactor;
2122

2223
private $needPrimaryEmail;
2324
private $needProfile;
@@ -100,6 +101,11 @@ public function withNamePrefixes(array $prefixes) {
100101
return $this;
101102
}
102103

104+
public function withIsEnrolledInMultiFactor($enrolled) {
105+
$this->isEnrolledInMultiFactor = $enrolled;
106+
return $this;
107+
}
108+
103109
public function needPrimaryEmail($need) {
104110
$this->needPrimaryEmail = $need;
105111
return $this;
@@ -337,6 +343,13 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
337343
$this->nameLike);
338344
}
339345

346+
if ($this->isEnrolledInMultiFactor !== null) {
347+
$where[] = qsprintf(
348+
$conn,
349+
'user.isEnrolledInMultiFactor = %d',
350+
(int)$this->isEnrolledInMultiFactor);
351+
}
352+
340353
return $where;
341354
}
342355

‎src/applications/people/query/PhabricatorPeopleSearchEngine.php

+48-11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public function newQuery() {
1818
}
1919

2020
protected function buildCustomSearchFields() {
21-
return array(
21+
$fields = array(
2222
id(new PhabricatorSearchStringListField())
2323
->setLabel(pht('Usernames'))
2424
->setKey('usernames')
@@ -84,18 +84,36 @@ protected function buildCustomSearchFields() {
8484
pht(
8585
'Pass true to find only users awaiting administrative approval, '.
8686
'or false to omit these users.')),
87-
id(new PhabricatorSearchDateField())
88-
->setKey('createdStart')
89-
->setLabel(pht('Joined After'))
90-
->setDescription(
91-
pht('Find user accounts created after a given time.')),
92-
id(new PhabricatorSearchDateField())
93-
->setKey('createdEnd')
94-
->setLabel(pht('Joined Before'))
87+
);
88+
89+
$viewer = $this->requireViewer();
90+
if ($viewer->getIsAdmin()) {
91+
$fields[] = id(new PhabricatorSearchThreeStateField())
92+
->setLabel(pht('Has MFA'))
93+
->setKey('mfa')
94+
->setOptions(
95+
pht('(Show All)'),
96+
pht('Show Only Users With MFA'),
97+
pht('Hide Users With MFA'))
9598
->setDescription(
96-
pht('Find user accounts created before a given time.')),
99+
pht(
100+
'Pass true to find only users who are enrolled in MFA, or false '.
101+
'to omit these users.'));
102+
}
97103

98-
);
104+
$fields[] = id(new PhabricatorSearchDateField())
105+
->setKey('createdStart')
106+
->setLabel(pht('Joined After'))
107+
->setDescription(
108+
pht('Find user accounts created after a given time.'));
109+
110+
$fields[] = id(new PhabricatorSearchDateField())
111+
->setKey('createdEnd')
112+
->setLabel(pht('Joined Before'))
113+
->setDescription(
114+
pht('Find user accounts created before a given time.'));
115+
116+
return $fields;
99117
}
100118

101119
protected function getDefaultFieldOrder() {
@@ -151,6 +169,19 @@ protected function buildQueryFromParameters(array $map) {
151169
$query->withIsApproved(!$map['needsApproval']);
152170
}
153171

172+
if (idx($map, 'mfa') !== null) {
173+
$viewer = $this->requireViewer();
174+
if (!$viewer->getIsAdmin()) {
175+
throw new PhabricatorSearchConstraintException(
176+
pht(
177+
'The "Has MFA" query constraint may only be used by '.
178+
'administrators, to prevent attackers from using it to target '.
179+
'weak accounts.'));
180+
}
181+
182+
$query->withIsEnrolledInMultiFactor($map['mfa']);
183+
}
184+
154185
if ($map['createdStart']) {
155186
$query->withDateCreatedAfter($map['createdStart']);
156187
}
@@ -254,6 +285,12 @@ protected function renderResultList(
254285
$item->addIcon('fa-envelope-o', pht('Mailing List'));
255286
}
256287

288+
if ($viewer->getIsAdmin()) {
289+
if ($user->getIsEnrolledInMultiFactor()) {
290+
$item->addIcon('fa-lock', pht('Has MFA'));
291+
}
292+
}
293+
257294
if ($viewer->getIsAdmin()) {
258295
$user_id = $user->getID();
259296
if ($is_approval) {

‎src/applications/search/controller/PhabricatorApplicationSearchController.php

+2
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ private function processSearchRequest() {
331331
'query parameters and correct errors.');
332332
} catch (PhutilSearchQueryCompilerSyntaxException $ex) {
333333
$exec_errors[] = $ex->getMessage();
334+
} catch (PhabricatorSearchConstraintException $ex) {
335+
$exec_errors[] = $ex->getMessage();
334336
}
335337

336338
// The engine may have encountered additional errors during rendering;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
final class PhabricatorSearchConstraintException
4+
extends Exception {}

0 commit comments

Comments
 (0)
Failed to load comments.