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.
Type type() const const
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(double n, char format, int precision)
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 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
QString arg(Args &&... args) const const
QString toString() const const
T value(const Key &key, const T &defaultValue) const const
QByteArray toUtf8() const const