Skip to content
This repository was archived by the owner on Mar 3, 2020. It is now read-only.

Commit ac64f55

Browse files
javutogsingh93
authored andcommitted
Registration enforcing strong passwords (#442)
* Password types in admin * Fully functional password complexity enforcement for registration * lowercase word in text * Adding test for password types regex and fixing all errors for hh_client * Updating outdated schema for tests
1 parent 6d04149 commit ac64f55

File tree

12 files changed

+171
-46
lines changed

12 files changed

+171
-46
lines changed

database/schema.sql

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ INSERT INTO `configuration` (field, value, description) VALUES("ldap_domain_suff
207207
INSERT INTO `configuration` (field, value, description) VALUES("login", "1", "(Boolean) Ability to login");
208208
INSERT INTO `configuration` (field, value, description) VALUES("login_select", "0", "(Boolean) Login selecting the team");
209209
INSERT INTO `configuration` (field, value, description) VALUES("login_strongpasswords", "0", "(Boolean) Enforce using strong passwords");
210-
INSERT INTO `configuration` (field, value, description) VALUES("password_type", "1", "(Integer) Type of passwords: See password_types");
210+
INSERT INTO `configuration` (field, value, description) VALUES("password_type", "1", "(Integer) Type of passwords: See table password_types");
211211
INSERT INTO `configuration` (field, value, description) VALUES("default_bonus", "30", "(Integer) Default value for bonus in levels");
212212
INSERT INTO `configuration` (field, value, description) VALUES("default_bonusdec", "10", "(Integer) Default bonus decrement in levels");
213213
INSERT INTO `configuration` (field, value, description) VALUES("language", "en", "(String) Language of the system");
@@ -223,17 +223,18 @@ DROP TABLE IF EXISTS `password_types`;
223223
CREATE TABLE `password_types` (
224224
`id` int(11) NOT NULL AUTO_INCREMENT,
225225
`field` varchar(100) NOT NULL,
226+
`value` text NOT NULL,
226227
`description` text NOT NULL,
227-
`regex` text NOT NULL,
228228
PRIMARY KEY (`id`),
229229
UNIQUE KEY `field` (`field`)
230230
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
231231
/*!40101 SET character_set_client = @saved_cs_client */;
232232

233233
LOCK TABLES `password_types` WRITE;
234-
INSERT INTO `password_types` (field, regex, description) VALUES("1", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[0-9]).*$/", "Length > 8, [a-z] and [0-9]");
235-
INSERT INTO `password_types` (field, regex, description) VALUES("2", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$/", "Length > 8, [a-z], [A-Z] and [0-9]");
236-
INSERT INTO `password_types` (field, regex, description) VALUES("3", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*\W).*$/", "Length > 8, [a-z], [A-Z], [0-9] and Special chars");
234+
INSERT INTO `password_types` (field, value, description) VALUES("1", "/.+/", "Length > 0");
235+
INSERT INTO `password_types` (field, value, description) VALUES("2", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[0-9]).*$/", "Length > 8, [a-z] and [0-9]");
236+
INSERT INTO `password_types` (field, value, description) VALUES("3", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$/", "Length > 8, [a-z], [A-Z] and [0-9]");
237+
INSERT INTO `password_types` (field, value, description) VALUES("4", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[\\W]+).*$/", "Length > 8, [a-z], [A-Z], [0-9] and Special chars");
237238

238239
UNLOCK TABLES;
239240

database/test_schema.sql

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ CREATE TABLE `teams` (
118118
`active` tinyint(1) NOT NULL DEFAULT 1,
119119
`name` text NOT NULL,
120120
`password_hash` text NOT NULL,
121-
`points` int(11) NOT NULL,
121+
`points` int(11) NOT NULL DEFAULT 0,
122122
`last_score` timestamp NOT NULL,
123123
`logo` text NOT NULL,
124-
`admin` tinyint(1) NOT NULL,
125-
`protected` tinyint(1) NOT NULL,
124+
`admin` tinyint(1) NOT NULL DEFAULT 0,
125+
`protected` tinyint(1) NOT NULL DEFAULT 0,
126126
`visible` tinyint(1) NOT NULL DEFAULT 1,
127127
`created_ts` timestamp NOT NULL DEFAULT 0,
128128
PRIMARY KEY (`id`)
@@ -207,7 +207,7 @@ INSERT INTO `configuration` (field, value, description) VALUES("ldap_domain_suff
207207
INSERT INTO `configuration` (field, value, description) VALUES("login", "1", "(Boolean) Ability to login");
208208
INSERT INTO `configuration` (field, value, description) VALUES("login_select", "0", "(Boolean) Login selecting the team");
209209
INSERT INTO `configuration` (field, value, description) VALUES("login_strongpasswords", "0", "(Boolean) Enforce using strong passwords");
210-
INSERT INTO `configuration` (field, value, description) VALUES("password_type", "1", "(Integer) Type of passwords: See password_types");
210+
INSERT INTO `configuration` (field, value, description) VALUES("password_type", "1", "(Integer) Type of passwords: See table password_types");
211211
INSERT INTO `configuration` (field, value, description) VALUES("default_bonus", "30", "(Integer) Default value for bonus in levels");
212212
INSERT INTO `configuration` (field, value, description) VALUES("default_bonusdec", "10", "(Integer) Default bonus decrement in levels");
213213
INSERT INTO `configuration` (field, value, description) VALUES("language", "en", "(String) Language of the system");
@@ -223,17 +223,18 @@ DROP TABLE IF EXISTS `password_types`;
223223
CREATE TABLE `password_types` (
224224
`id` int(11) NOT NULL AUTO_INCREMENT,
225225
`field` varchar(100) NOT NULL,
226+
`value` text NOT NULL,
226227
`description` text NOT NULL,
227-
`regex` text NOT NULL,
228228
PRIMARY KEY (`id`),
229229
UNIQUE KEY `field` (`field`)
230230
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
231231
/*!40101 SET character_set_client = @saved_cs_client */;
232232

233233
LOCK TABLES `password_types` WRITE;
234-
INSERT INTO `password_types` (field, regex, description) VALUES("1", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[0-9]).*$/", "Length > 8, [a-z] and [0-9]");
235-
INSERT INTO `password_types` (field, regex, description) VALUES("2", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$/", "Length > 8, [a-z], [A-Z] and [0-9]");
236-
INSERT INTO `password_types` (field, regex, description) VALUES("3", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*\W).*$/", "Length > 8, [a-z], [A-Z], [0-9] and Special chars");
234+
INSERT INTO `password_types` (field, value, description) VALUES("1", "/.+/", "Length > 0");
235+
INSERT INTO `password_types` (field, value, description) VALUES("2", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[0-9]).*$/", "Length > 8, [a-z] and [0-9]");
236+
INSERT INTO `password_types` (field, value, description) VALUES("3", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$/", "Length > 8, [a-z], [A-Z] and [0-9]");
237+
INSERT INTO `password_types` (field, value, description) VALUES("4", "/.*^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[\\W]+).*$/", "Length > 8, [a-z], [A-Z], [0-9] and Special chars");
237238

238239
UNLOCK TABLES;
239240

src/controllers/AdminController.php

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,25 @@ class="fb--conf--registration_type"
149149
return $select;
150150
}
151151

152+
// TODO: Translate password types
153+
private async function genStrongPasswordsSelect(): Awaitable<:xhp> {
154+
$types = await Configuration::genAllPasswordTypes();
155+
$config = await Configuration::genCurrentPasswordType();
156+
$select = <select name="fb--conf--password_type"></select>;
157+
foreach ($types as $type) {
158+
$select->appendChild(
159+
<option
160+
class="fb--conf--password_type"
161+
value={strval($type->getField())}
162+
selected={($type->getField() === $config->getField())}>
163+
{$type->getDescription()}
164+
</option>
165+
);
166+
}
167+
168+
return $select;
169+
}
170+
152171
private async function genConfigurationDurationSelect(): Awaitable<:xhp> {
153172
$config = await Configuration::gen('game_duration_unit');
154173
$duration_unit = $config->getValue();
@@ -408,13 +427,26 @@ class="fb-cta cta--yellow"
408427
'configuration_duration_select' =>
409428
$this->genConfigurationDurationSelect(),
410429
'language_select' => $this->genLanguageSelect(),
430+
'password_types_select' => $this->genStrongPasswordsSelect(),
411431
};
412432
$results = await \HH\Asio\m($awaitables);
413433

414434
$registration_type_select = $results['registration_type_select'];
415435
$configuration_duration_select =
416436
$results['configuration_duration_select'];
417437
$language_select = $results['language_select'];
438+
$password_types_select = $results['password_types_select'];
439+
440+
$login_strongpasswords = await Configuration::gen('login_strongpasswords');
441+
if ($login_strongpasswords->getValue() === '0') { // Strong passwords are not enforced
442+
$strong_passwords = <div></div>;
443+
} else {
444+
$strong_passwords =
445+
<div class="form-el el--block-label">
446+
<label>{tr('Password Types')}</label>
447+
{$password_types_select}
448+
</div>;
449+
}
418450

419451
return
420452
<div>
@@ -519,56 +551,59 @@ class="fb-cta cta--yellow"
519551
</div>
520552
</header>
521553
<div class="fb-column-container">
522-
<div class="col col-pad col-1-2">
554+
<div class="col col-pad col-1-3">
523555
<div class="form-el el--block-label">
524-
<label>{tr('Strong Passwords')}</label>
556+
<label>{tr('Team Selection')}</label>
525557
<div class="admin-section-toggle radio-inline">
526558
<input
527559
type="radio"
528-
name="fb--conf--login_strongpasswords"
529-
id="fb--conf--login_strongpasswords--on"
530-
checked={$strong_passwords_on}
560+
name="fb--conf--login_select"
561+
id="fb--conf--login_select--on"
562+
checked={$login_select_on}
531563
/>
532-
<label for="fb--conf--login_strongpasswords--on">
564+
<label for="fb--conf--login_select--on">
533565
{tr('On')}
534566
</label>
535567
<input
536568
type="radio"
537-
name="fb--conf--login_strongpasswords"
538-
id="fb--conf--login_strongpasswords--off"
539-
checked={$strong_passwords_off}
569+
name="fb--conf--login_select"
570+
id="fb--conf--login_select--off"
571+
checked={$login_select_off}
540572
/>
541-
<label for="fb--conf--login_strongpasswords--off">
573+
<label for="fb--conf--login_select--off">
542574
{tr('Off')}
543575
</label>
544576
</div>
545577
</div>
546578
</div>
547-
<div class="col col-pad col-2-2">
579+
<div class="col col-pad col-1-3">
548580
<div class="form-el el--block-label">
549-
<label>{tr('Team Selection')}</label>
581+
<label>{tr('Strong Passwords')}</label>
550582
<div class="admin-section-toggle radio-inline">
551583
<input
552584
type="radio"
553-
name="fb--conf--login_select"
554-
id="fb--conf--login_select--on"
555-
checked={$login_select_on}
585+
name="fb--conf--login_strongpasswords"
586+
id="fb--conf--login_strongpasswords--on"
587+
checked={$strong_passwords_on}
556588
/>
557-
<label for="fb--conf--login_select--on">
589+
<label for="fb--conf--login_strongpasswords--on">
558590
{tr('On')}
559591
</label>
560592
<input
561593
type="radio"
562-
name="fb--conf--login_select"
563-
id="fb--conf--login_select--off"
564-
checked={$login_select_off}
594+
name="fb--conf--login_strongpasswords"
595+
id="fb--conf--login_strongpasswords--off"
596+
checked={$strong_passwords_off}
565597
/>
566-
<label for="fb--conf--login_select--off">
598+
<label for="fb--conf--login_strongpasswords--off">
567599
{tr('Off')}
568600
</label>
569601
</div>
570602
</div>
571603
</div>
604+
<div class="col col-pad col-2-3">
605+
{$strong_passwords}
606+
</div>
572607
</div>
573608
</section>
574609
<section class="admin-box">
@@ -2852,7 +2887,8 @@ class={$highlighted_color}
28522887
if (count($failures) > 0) {
28532888
$failures_tbody = <tbody></tbody>;
28542889
foreach ($failures as $failure) {
2855-
if (!Level::genCheckStatus($failure->getLevelId())) {
2890+
$level_genCheckStatus = await Level::genCheckStatus($failure->getLevelId());
2891+
if (!$level_genCheckStatus) {
28562892
continue;
28572893
}
28582894
$level = await Level::gen($failure->getLevelId());

src/controllers/IndexController.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ class="fb-main page--team-registration full-height fb-scroll">
345345
name="teamname"
346346
type="text"
347347
maxlength={20}
348+
autofocus={true}
348349
/>
349350
{$ldap_domain_suffix}
350351
</div>
@@ -451,13 +452,18 @@ class="fb-main page--registration full-height fb-scroll">
451452
name="teamname"
452453
type="text"
453454
maxlength={20}
455+
autofocus={true}
454456
/>
455457
{$ldap_domain_suffix}
456458
</div>
457459
<div class="form-el el--text">
458460
<label for="">{tr('Password')}</label>
459461
<input autocomplete="off" name="password" type="password" />
460462
</div>
463+
<div id="password_error" class="el--text completely-hidden">
464+
<label for=""></label>
465+
<h6 style="color:red;">{tr('Password is too simple')}</h6>
466+
</div>
461467
{$token_field}
462468
</fieldset>
463469
<div class="fb-choose-emblem">

src/controllers/ajax/IndexAjaxController.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,18 @@ protected function getActions(): array<string> {
115115
return Utils::error_response('Registration failed', 'registration');
116116
}
117117

118+
// Check if strongs passwords are enforced
119+
$login_strongpasswords = await Configuration::gen('login_strongpasswords');
120+
if ($login_strongpasswords->getValue() !== '0') {
121+
$password_type = await Configuration::genCurrentPasswordType();
122+
if (!preg_match(strval($password_type->getValue()), $password)) {
123+
return Utils::error_response('Password too simple', 'registration');
124+
}
125+
}
126+
118127
// Check if ldap is enabled and verify credentials if successful
119128
$ldap = await Configuration::gen('ldap');
129+
$ldap_password = '';
120130
if ($ldap->getValue() === '1') {
121131
// Get server information from configuration
122132
$ldap_server = await Configuration::gen('ldap_server');
@@ -145,10 +155,10 @@ protected function getActions(): array<string> {
145155
// This will help avoid leaking users ldap passwords if the server's database
146156
// is compromised.
147157
$ldap_password = $password;
148-
$password = gmp_strval(
158+
$password = strval(gmp_strval(
149159
gmp_init(bin2hex(openssl_random_pseudo_bytes(16)), 16),
150160
62,
151-
);
161+
));
152162
}
153163

154164
// Check if tokenized registration is enabled
@@ -205,9 +215,11 @@ protected function getActions(): array<string> {
205215
await Token::genUse($token, $team_id);
206216
}
207217
// Login the team
208-
if ($ldap->getValue() === '1')
209-
return await $this->genLoginTeam($team_id, $ldap_password); else
218+
if ($ldap->getValue() === '1') {
219+
return await $this->genLoginTeam($team_id, $ldap_password);
220+
} else {
210221
return await $this->genLoginTeam($team_id, $password);
222+
}
211223
} else {
212224
return Utils::error_response('Registration failed', 'registration');
213225
}

src/language/lang_en.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@
8585
'Password',
8686
'Choose an Emblem' =>
8787
'Choose an Emblem',
88+
'or Upload your own' =>
89+
'or Upload your own',
90+
'Clear your custom emblem to use a default emblem.' =>
91+
'Clear your custom emblem to use a default emblem.',
92+
'Password is too simple' =>
93+
'Password is too simple',
8894
'Sign Up' =>
8995
'Sign Up',
9096
'Register to play Capture The Flag here. Once you have registered, you will be logged in.' =>

src/models/Configuration.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,35 @@ public function getDescription(): string {
7878
return intval(idx(firstx($result->mapRows()), 'COUNT(*)')) > 0;
7979
}
8080

81+
// All the password types.
82+
public static async function genAllPasswordTypes(
83+
): Awaitable<array<Configuration>> {
84+
$db = await self::genDb();
85+
$result = await $db->queryf('SELECT * FROM password_types');
86+
87+
$types = array();
88+
foreach ($result->mapRows() as $row) {
89+
$types[] = self::configurationFromRow($row->toArray());
90+
}
91+
92+
return $types;
93+
}
94+
95+
// Current password type.
96+
public static async function genCurrentPasswordType(
97+
): Awaitable<Configuration> {
98+
$db = await self::genDb();
99+
$db_result = await $db->queryf(
100+
'SELECT * FROM password_types WHERE field = (SELECT value FROM configuration WHERE field = %s) LIMIT 1',
101+
'password_type'
102+
);
103+
104+
invariant($db_result->numRows() === 1, 'Expected exactly one result');
105+
$result = firstx($db_result->mapRows())->toArray();
106+
107+
return self::configurationFromRow($result);
108+
}
109+
81110
// All the configuration.
82111
public static async function genAllConfiguration(
83112
): Awaitable<array<Configuration>> {

src/models/Logo.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ private static function logoFromRow(Map<string, string> $row): Logo {
269269
// Get image properties and verify mimetype
270270
$base64_data = str_replace(' ', '+', $base64_data);
271271
$binary_data = base64_decode(str_replace(' ', '+', $base64_data));
272+
/* HH_IGNORE_ERROR[2049] */
273+
/* HH_IGNORE_ERROR[4107] */
272274
$image_info = getimagesizefromstring($binary_data);
273275

274276
$mimetype = $image_info[2];

src/static/js/admin.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,10 @@ function toggleConfiguration(radio_id) {
909909
field: radio_action,
910910
value: action_value
911911
};
912-
if (radio_action) {
912+
var refresh_fields = ['login_strongpasswords'];
913+
if (refresh_fields.indexOf(radio_action) !== -1) {
914+
sendAdminRequest(toggle_data, true);
915+
} else {
913916
sendAdminRequest(toggle_data, false);
914917
}
915918
}
@@ -920,9 +923,10 @@ function changeConfiguration(field, value) {
920923
field: field,
921924
value: value
922925
};
923-
if (field === 'registration_type' || field === 'language') {
926+
var refresh_fields = ['registration_type', 'language'];
927+
if (refresh_fields.indexOf(field) !== -1) {
924928
sendAdminRequest(conf_data, true);
925-
}else {
929+
} else {
926930
sendAdminRequest(conf_data, false);
927931
}
928932
}

0 commit comments

Comments
 (0)