cutelyst 4.0.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
validatorpwquality.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2018-2023 Matthias Fehring <mf@huessenbergnetz.de>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6#include "validatorpwquality_p.h"
7
8#include <pwquality.h>
9
10#include <QLoggingCategory>
11
12using namespace Cutelyst;
13
15 int threshold,
16 const QVariant &options,
17 const QString &userName,
18 const QString &oldPassword,
19 const ValidatorMessages &messages)
20 : ValidatorRule(*new ValidatorPwQualityPrivate(field,
21 threshold,
22 options,
23 userName,
24 oldPassword,
25 messages))
26{
27}
28
30
31int ValidatorPwQuality::validate(const QString &value,
32 const QVariant &options,
33 const QString &oldPassword,
34 const QString &user)
35{
36 int rv = 0;
37
38 if (!value.isEmpty()) {
39
40 pwquality_settings_t *pwq = pwquality_default_settings();
41 if (pwq) {
42
43 bool optionsSet = false;
44 if (options.isValid()) {
45 if (options.typeId() == QMetaType::QVariantMap) {
46 const QVariantMap map = options.toMap();
47 if (!map.empty()) {
48 auto i = map.constBegin();
49 while (i != map.constEnd()) {
50 const QString opt = i.key() + QLatin1Char('=') + i.value().toString();
51 const int orv = pwquality_set_option(pwq, opt.toUtf8().constData());
52 if (orv != 0) {
53 QList<char> buf(ValidatorPwQualityPrivate::errStrBufSize);
54 qCWarning(C_VALIDATOR).noquote().nospace()
55 << "ValidatorPwQuality: Failed to set pwquality option " << opt
56 << ": "
57 << pwquality_strerror(buf.data(), buf.size(), orv, nullptr);
58 }
59 ++i;
60 }
61 optionsSet = true;
62 }
63 } else if (options.typeId() == QMetaType::QString) {
64 const QString configFile = options.toString();
65 if (!configFile.isEmpty()) {
66 if (C_VALIDATOR().isWarningEnabled()) {
67 void *auxerror = nullptr;
68 const int rcrv = pwquality_read_config(
69 pwq, configFile.toUtf8().constData(), &auxerror);
70 if (rcrv != 0) {
71 QList<char> buf(ValidatorPwQualityPrivate::errStrBufSize);
72 qCWarning(C_VALIDATOR).noquote().nospace()
73 << "ValidatorPwQuality: Failed to read configuration file "
74 << configFile << ": "
75 << pwquality_strerror(buf.data(), buf.size(), rcrv, auxerror);
76 }
77 } else {
78 pwquality_read_config(pwq, configFile.toUtf8().constData(), nullptr);
79 }
80 optionsSet = true;
81 }
82 }
83 }
84
85 if (!optionsSet) {
86 if (C_VALIDATOR().isWarningEnabled()) {
87 void *auxerror = nullptr;
88 const int rcrv = pwquality_read_config(pwq, nullptr, &auxerror);
89 if (rcrv != 0) {
90 QList<char> buf(ValidatorPwQualityPrivate::errStrBufSize);
91 qCWarning(C_VALIDATOR).noquote()
92 << "VaidatorPwQuality: Failed to read default configuration file:"
93 << pwquality_strerror(buf.data(), buf.size(), rcrv, auxerror);
94 }
95 } else {
96 pwquality_read_config(pwq, nullptr, nullptr);
97 }
98 }
99
100 const QByteArray pwba = value.toUtf8();
101 const char *pw = pwba.constData();
102 const QByteArray opwba = oldPassword.toUtf8();
103 const char *opw = opwba.isEmpty() ? nullptr : opwba.constData();
104 const QByteArray uba = user.toUtf8();
105 const char *u = uba.isEmpty() ? nullptr : uba.constData();
106
107 rv = pwquality_check(pwq, pw, opw, u, nullptr);
108
109 pwquality_free_settings(pwq);
110
111 } else {
112 rv = PWQ_ERROR_MEM_ALLOC;
113 }
114 } else {
115 rv = PWQ_ERROR_EMPTY_PASSWORD;
116 }
117
118 return rv;
119}
120
122 int returnValue,
123 const QString &label,
124 int threshold)
125{
126 QString error;
127
128 if (label.isEmpty()) {
129 switch (returnValue) {
130 case PWQ_ERROR_MEM_ALLOC:
131 error =
132 c->translate("Cutelyst::ValidatorPwQuality",
133 "Password quality check failed because of a memory allocation error.");
134 break;
135 case PWQ_ERROR_SAME_PASSWORD:
136 error = c->translate("Cutelyst::ValidatorPwQuality",
137 "The password is the same as the old one.");
138 break;
139 case PWQ_ERROR_PALINDROME:
140 error = c->translate("Cutelyst::ValidatorPwQuality", "The password is a palindrome.");
141 break;
142 case PWQ_ERROR_CASE_CHANGES_ONLY:
143 error = c->translate("Cutelyst::ValidatorPwQuality",
144 "The password differs with case changes only.");
145 break;
146 case PWQ_ERROR_TOO_SIMILAR:
147 error = c->translate("Cutelyst::ValidatorPwQuality",
148 "The password is too similar to the old one.");
149 break;
150 case PWQ_ERROR_USER_CHECK:
151 error = c->translate("Cutelyst::ValidatorPwQuality",
152 "The password contains the user name in some form.");
153 break;
154 case PWQ_ERROR_GECOS_CHECK:
155 error = c->translate(
156 "Cutelyst::ValidatorPwQuality",
157 "The password contains words from the real name of the user in some form.");
158 break;
159 case PWQ_ERROR_BAD_WORDS:
160 error = c->translate("Cutelyst::ValidatorPwQuality",
161 "The password contains forbidden words in some form.");
162 break;
163 case PWQ_ERROR_MIN_DIGITS:
164 error = c->translate("Cutelyst::ValidatorPwQuality",
165 "The password contains too few digits.");
166 break;
167 case PWQ_ERROR_MIN_UPPERS:
168 error = c->translate("Cutelyst::ValidatorPwQuality",
169 "The password contains too few uppercase letters.");
170 break;
171 case PWQ_ERROR_MIN_LOWERS:
172 error = c->translate("Cutelyst::ValidatorPwQuality",
173 "The password contains too few lowercase letters.");
174 break;
175 case PWQ_ERROR_MIN_OTHERS:
176 error = c->translate("Cutelyst::ValidatorPwQuality",
177 "The password contains too few non-alphanumeric characters.");
178 break;
179 case PWQ_ERROR_MIN_LENGTH:
180 error = c->translate("Cutelyst::ValidatorPwQuality", "The password is too short.");
181 break;
182 case PWQ_ERROR_ROTATED:
183 error = c->translate("Cutelyst::ValidatorPwQuality",
184 "The password is just the rotated old one.");
185 break;
186 case PWQ_ERROR_MIN_CLASSES:
187 error = c->translate("Cutelyst::ValidatorPwQuality",
188 "The password does not contain enough different character types.");
189 break;
190 case PWQ_ERROR_MAX_CONSECUTIVE:
191 error = c->translate("Cutelyst::ValidatorPwQuality",
192 "The password contains too many same characters consecutively.");
193 break;
194 case PWQ_ERROR_MAX_CLASS_REPEAT:
195 error = c->translate(
196 "Cutelyst::ValidatorPwQuality",
197 "The password contains too many characters of the same type consecutively.");
198 break;
199 case PWQ_ERROR_MAX_SEQUENCE:
200 error = c->translate("Cutelyst::ValidatorPwQuality",
201 "The password contains too long a monotonous string.");
202 break;
203 case PWQ_ERROR_EMPTY_PASSWORD:
204 error = c->translate("Cutelyst::ValidatorPwQuality", "No password supplied.");
205 break;
206 case PWQ_ERROR_RNG:
207 error = c->translate("Cutelyst::ValidatorPwQuality",
208 "Password quality check failed because we cannot obtain random "
209 "numbers from the RNG device.");
210 break;
211 case PWQ_ERROR_CRACKLIB_CHECK:
212 error = c->translate("Cutelyst::ValidatorPwQuality",
213 "The password fails the dictionary check.");
214 break;
215 case PWQ_ERROR_UNKNOWN_SETTING:
216 error = c->translate("Cutelyst::ValidatorPwQuality",
217 "Password quality check failed because of an unknown setting.");
218 break;
219 case PWQ_ERROR_INTEGER:
220 error = c->translate(
221 "Cutelyst::ValidatorPwQuality",
222 "Password quality check failed because of a bad integer value in the settings.");
223 break;
224 case PWQ_ERROR_NON_INT_SETTING:
225 error = c->translate("Cutelyst::ValidatorPwQuality",
226 "Password quality check failed because of a settings entry is not "
227 "of integer type.");
228 break;
229 case PWQ_ERROR_NON_STR_SETTING:
230 error = c->translate(
231 "Cutelyst::ValidatorPwQuality",
232 "Password quality check failed because of a settings entry is not of string type.");
233 break;
234 case PWQ_ERROR_CFGFILE_OPEN:
235 error = c->translate(
236 "Cutelyst::ValidatorPwQuality",
237 "Password quality check failed because opening the configuration file failed.");
238 break;
239 case PWQ_ERROR_CFGFILE_MALFORMED:
240 error = c->translate(
241 "Cutelyst::ValidatorPwQuality",
242 "Password quality check failed because the configuration file is malformed.");
243 break;
244 case PWQ_ERROR_FATAL_FAILURE:
245 error = c->translate("Cutelyst::ValidatorPwQuality",
246 "Password quality check failed because of a fatal failure.");
247 break;
248 default:
249 {
250 if (returnValue < 0) {
251 error = c->translate("Cutelyst::ValidatorPwQuality",
252 "Password quality check failed because of an unknown error.");
253 } else {
254 if (returnValue < threshold) {
255 error = c->translate(
256 "Cutelyst::ValidatorPwQuality",
257 "The password quality score of %1 is below the threshold of %2.")
258 .arg(QString::number(returnValue), QString::number(threshold));
259 }
260 }
261 } break;
262 }
263 } else {
264 switch (returnValue) {
265 case PWQ_ERROR_MEM_ALLOC:
266 error = c->translate("Cutelyst::ValidatorPwQuality",
267 "Password quality check for the “%1“ field failed because of a "
268 "memory allocation error.")
269 .arg(label);
270 break;
271 case PWQ_ERROR_SAME_PASSWORD:
272 error = c->translate("Cutelyst::ValidatorPwQuality",
273 "The password in the “%1” field is the same as the old one.")
274 .arg(label);
275 break;
276 case PWQ_ERROR_PALINDROME:
277 error = c->translate("Cutelyst::ValidatorPwQuality",
278 "The password in the “%1” field is a palindrome.")
279 .arg(label);
280 break;
281 case PWQ_ERROR_CASE_CHANGES_ONLY:
282 error = c->translate("Cutelyst::ValidatorPwQuality",
283 "The password in the “%1” field differs with case changes only.")
284 .arg(label);
285 break;
286 case PWQ_ERROR_TOO_SIMILAR:
287 error = c->translate("Cutelyst::ValidatorPwQuality",
288 "The password in the “%1” field is too similar to the old one.")
289 .arg(label);
290 break;
291 case PWQ_ERROR_USER_CHECK:
292 error =
293 c->translate("Cutelyst::ValidatorPwQuality",
294 "The password in the “%1” field contains the user name in some form.")
295 .arg(label);
296 break;
297 case PWQ_ERROR_GECOS_CHECK:
298 error = c->translate("Cutelyst::ValidatorPwQuality",
299 "The password in the “%1” field contains words from the real name "
300 "of the user name in some form.")
301 .arg(label);
302 break;
303 case PWQ_ERROR_BAD_WORDS:
304 error = c->translate(
305 "Cutelyst::ValidatorPwQuality",
306 "The password in the “%1” field contains forbidden words in some form.")
307 .arg(label);
308 break;
309 case PWQ_ERROR_MIN_DIGITS:
310 error = c->translate("Cutelyst::ValidatorPwQuality",
311 "The password in the “%1” field contains too few digits.")
312 .arg(label);
313 break;
314 case PWQ_ERROR_MIN_UPPERS:
315 error =
316 c->translate("Cutelyst::ValidatorPwQuality",
317 "The password in the “%1” field contains too few uppercase letters.")
318 .arg(label);
319 break;
320 case PWQ_ERROR_MIN_LOWERS:
321 error =
322 c->translate("Cutelyst::ValidatorPwQuality",
323 "The password in the “%1” field contains too few lowercase letters.")
324 .arg(label);
325 break;
326 case PWQ_ERROR_MIN_OTHERS:
327 error =
328 c->translate(
329 "Cutelyst::ValidatorPwQuality",
330 "The password in the “%1” field contains too few non-alphanumeric characters.")
331 .arg(label);
332 break;
333 case PWQ_ERROR_MIN_LENGTH:
334 error = c->translate("Cutelyst::ValidatorPwQuality",
335 "The password in the “%1” field is too short.")
336 .arg(label);
337 break;
338 case PWQ_ERROR_ROTATED:
339 error = c->translate("Cutelyst::ValidatorPwQuality",
340 "The password in the “%1” field is just the rotated old one.")
341 .arg(label);
342 break;
343 case PWQ_ERROR_MIN_CLASSES:
344 error = c->translate(
345 "Cutelyst::ValidatorPwQuality",
346 "The password in the “%1” field does not contain enough character types.")
347 .arg(label);
348 break;
349 case PWQ_ERROR_MAX_CONSECUTIVE:
350 error = c->translate("Cutelyst::ValidatorPwQuality",
351 "The password in the “%1” field contains too many same characters "
352 "consecutively.")
353 .arg(label);
354 break;
355 case PWQ_ERROR_MAX_CLASS_REPEAT:
356 error = c->translate("Cutelyst::ValidatorPwQuality",
357 "The password in the “%1” field contains too many characters of "
358 "the same type consecutively.")
359 .arg(label);
360 break;
361 case PWQ_ERROR_MAX_SEQUENCE:
362 error = c->translate("Cutelyst::ValidatorPwQuality",
363 "The password in the “%1” field contains contains too long a "
364 "monotonous string.")
365 .arg(label);
366 break;
367 case PWQ_ERROR_EMPTY_PASSWORD:
368 error = c->translate("Cutelyst::ValidatorPwQuality",
369 "No password supplied in the “%1” field.")
370 .arg(label);
371 break;
372 case PWQ_ERROR_RNG:
373 error = c->translate("Cutelyst::ValidatorPwQuality",
374 "Password quality check for the “%1“ field failed because we "
375 "cannot obtain random numbers from the RNG device.")
376 .arg(label);
377 break;
378 case PWQ_ERROR_CRACKLIB_CHECK:
379 error = c->translate("Cutelyst::ValidatorPwQuality",
380 "The password in the “%1” field fails the dictionary check.")
381 .arg(label);
382 break;
383 case PWQ_ERROR_UNKNOWN_SETTING:
384 error = c->translate("Cutelyst::ValidatorPwQuality",
385 "Password quality check for the “%1“ field failed because of an "
386 "unknown setting.")
387 .arg(label);
388 break;
389 case PWQ_ERROR_INTEGER:
390 error = c->translate("Cutelyst::ValidatorPwQuality",
391 "Password quality check for the “%1“ field failed because of a "
392 "bad integer value in the settings.")
393 .arg(label);
394 break;
395 case PWQ_ERROR_NON_INT_SETTING:
396 error = c->translate("Cutelyst::ValidatorPwQuality",
397 "Password quality check for the “%1“ field failed because of a "
398 "settings entry is not of integer type.")
399 .arg(label);
400 break;
401 case PWQ_ERROR_NON_STR_SETTING:
402 error = c->translate("Cutelyst::ValidatorPwQuality",
403 "Password quality check for the “%1“ field failed because of a "
404 "settings entry is not of string type.")
405 .arg(label);
406 break;
407 case PWQ_ERROR_CFGFILE_OPEN:
408 error = c->translate("Cutelyst::ValidatorPwQuality",
409 "Password quality check for the “%1“ field failed because opening "
410 "the configuration file failed.")
411 .arg(label);
412 break;
413 case PWQ_ERROR_CFGFILE_MALFORMED:
414 error = c->translate("Cutelyst::ValidatorPwQuality",
415 "Password quality check for the “%1“ field failed because the "
416 "configuration file is malformed.")
417 .arg(label);
418 break;
419 case PWQ_ERROR_FATAL_FAILURE:
420 error =
421 c->translate(
422 "Cutelyst::ValidatorPwQuality",
423 "Password quality check for the “%1“ field failed because of a fatal failure.")
424 .arg(label);
425 break;
426 default:
427 {
428 if (returnValue < 0) {
429 error = c->translate("Cutelyst::ValidatorPwQuality",
430 "Password quality check for the “%1” field failed because of "
431 "an unknown error.")
432 .arg(label);
433 } else {
434 if (returnValue < threshold) {
435 error =
436 c->translate("Cutelyst::ValidatorPwQuality",
437 "The quality score of %1 for the password in the “%2” field "
438 "is below the threshold of %3.")
439 .arg(QString::number(returnValue), label, QString::number(threshold));
440 }
441 }
442 } break;
443 }
444 }
445
446 return error;
447}
448
450{
451 ValidatorReturnType result;
452
453 const QString v = value(params);
454
455 if (!v.isEmpty()) {
456 Q_D(const ValidatorPwQuality);
457 QVariant opts;
458 if (d->options.isValid()) {
459 if (d->options.typeId() == QMetaType::QVariantMap) {
460 opts = d->options;
461 } else if (d->options.typeId() == QMetaType::QString) {
462 const QString optString = d->options.toString();
463 if (c->stash().contains(optString)) {
464 opts = c->stash(optString);
465 } else {
466 opts = d->options;
467 }
468 }
469 }
470 QString un;
471 if (!d->userName.isEmpty()) {
472 un = params.value(d->userName);
473 if (un.isEmpty()) {
474 un = c->stash(d->userName).toString();
475 }
476 }
477 QString opw;
478 if (!d->oldPassword.isEmpty()) {
479 opw = params.value(d->oldPassword);
480 if (opw.isEmpty()) {
481 opw = c->stash(d->oldPassword).toString();
482 }
483 }
484 int rv = validate(v, opts, opw, un);
485 if (rv < d->threshold) {
486 result.errorMessage = validationError(c, rv);
487 if (C_VALIDATOR().isDebugEnabled()) {
488 if (rv < 0) {
489 QList<char> buf(ValidatorPwQualityPrivate::errStrBufSize);
490 qCDebug(C_VALIDATOR).noquote()
491 << debugString(c)
492 << pwquality_strerror(buf.data(), buf.size(), rv, nullptr);
493 } else {
494 qCDebug(C_VALIDATOR).noquote() << debugString(c) << "The quality score" << rv
495 << "is below the threshold of" << d->threshold;
496 }
497 }
498 } else {
499 qCDebug(C_VALIDATOR).noquote()
500 << "ValidatorPwQuality: \"" << v << "\" got a quality score of" << rv;
501 result.value = v;
502 }
503 }
504
505 return result;
506}
507
508QString ValidatorPwQuality::genericValidationError(Context *c, const QVariant &errorData) const
509{
510 Q_D(const ValidatorPwQuality);
511 return ValidatorPwQuality::errorString(c, errorData.toInt(), label(c), d->threshold);
512}
The Cutelyst Context.
Definition: context.h:38
void stash(const QVariantHash &unite)
Definition: context.cpp:553
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
Definition: context.cpp:477
Validates an input field with libpwquality to check password quality.
static QString errorString(Context *c, int returnValue, const QString &label=QString(), int threshold=0)
Returns a human readable string for the return value of ValidatorPwQuality::validate()
QString genericValidationError(Context *c, const QVariant &errorData) const override
Returns a generic error message if validation failed.
~ValidatorPwQuality() override
Deconstructs the ValidatorPwQuality.
ValidatorPwQuality(const QString &field, int threshold=ValidatorPwQuality::defaultThreshold, const QVariant &options=QVariant(), const QString &userName=QString(), const QString &oldPassword=QString(), const ValidatorMessages &messages=ValidatorMessages())
Constructs a new ValidatorPwQuality with the given parameters.
Base class for all validator rules.
QString label(Context *c) const
Returns the human readable field label used for generic error messages.
QString value(const ParamsMultiMap &params) const
Returns the value of the field from the input params.
QString validationError(Context *c, const QVariant &errorData=QVariant()) const
Returns a descriptive error message if validation failed.
QString debugString(Context *c) const
Returns a string that can be used for debug output if validation fails.
static int validate(const QString &value, const QVariant &options=QVariant(), const QString &oldPassword=QString(), const QString &user=QString())
Returns the password quality score for value.
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8
QMultiMap< QString, QString > ParamsMultiMap
Stores custom error messages and the input field label.
Contains the result of a single input parameter validation.
Definition: validatorrule.h:49