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