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 
12 using 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 {
31 }
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 }
Validates an input field with libpwquality to check password quality.
QString validationError(Context *c, const QVariant &errorData=QVariant()) const
Returns a descriptive error message if validation failed.
Stores custom error messages and the input field label.
bool isEmpty() const const
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 Context.
Definition: context.h:38
QString number(int n, int base)
int toInt(bool *ok) const const
void stash(const QVariantHash &unite)
Definition: context.cpp:566
~ValidatorPwQuality() override
Deconstructs the ValidatorPwQuality.
bool isEmpty() const const
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.
const char * constData() const const
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
Definition: context.cpp:490
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
QString genericValidationError(Context *c, const QVariant &errorData) const override
Returns a generic error message if validation failed.
Base class for all validator rules.
QString label(Context *c) const
Returns the human readable field label used for generic error messages.
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 value(const ParamsMultiMap &params) const
Returns the value of the field from the input params.
QMap< QString, QVariant > toMap() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString field() const
Returns the name of the field to validate.
bool isValid() const const
Contains the result of a single input parameter validation.
Definition: validatorrule.h:49
QVariant::Type type() const const
QString toString() const const
const T value(const Key &key, const T &defaultValue) const const
QByteArray toUtf8() const const