cutelyst  4.4.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 
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 
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  if (label.isEmpty()) {
127  switch (returnValue) {
128  case PWQ_ERROR_MEM_ALLOC:
129  //% "Password quality check failed because of a memory allocation error."
130  return c->qtTrId("cutelyst-valpwq-err-memalloc");
131  case PWQ_ERROR_SAME_PASSWORD:
132  //% "The password is the same as the old one."
133  return c->qtTrId("cutelyst-valpwq-err-samepass");
134  case PWQ_ERROR_PALINDROME:
135  //% "The password is a palindrome."
136  return c->qtTrId("cutelyst-valpwq-err-palindrome");
137  case PWQ_ERROR_CASE_CHANGES_ONLY:
138  //% "The password differs with case changes only from the old one."
139  return c->qtTrId("cutelyst-valpwq-err-casechangesonly");
140  case PWQ_ERROR_TOO_SIMILAR:
141  //% "The password is too similar to the old one."
142  return c->qtTrId("cutelyst-valpwq-err-toosimilar");
143  case PWQ_ERROR_USER_CHECK:
144  //% "The password contains the user name in some form."
145  return c->qtTrId("cutelyst-valpwq-err-usercheck");
146  case PWQ_ERROR_GECOS_CHECK:
147  //% "The password contains words from the real name of the user in some form."
148  return c->qtTrId("cutelyst-valpwq-err-gecoscheck");
149  case PWQ_ERROR_BAD_WORDS:
150  //% "The password contains forbidden words in some form."
151  return c->qtTrId("cutelyst-valpwq-err-badwords");
152  case PWQ_ERROR_MIN_DIGITS:
153  //% "The password does not contain enough digits."
154  return c->qtTrId("cutelyst-valpwq-err-mindigits");
155  case PWQ_ERROR_MIN_UPPERS:
156  //% "The password does not contain enough uppercase letters."
157  return c->qtTrId("cutelyst-valpwq-err-minuppers");
158  case PWQ_ERROR_MIN_LOWERS:
159  //% "The password does not contain enough lowercase letters."
160  return c->qtTrId("cutelyst-valpwq-err-minlowers");
161  case PWQ_ERROR_MIN_OTHERS:
162  //% "The password does not contain enough non-alphanumeric characters."
163  return c->qtTrId("cutelyst-valpwq-err-minothers");
164  case PWQ_ERROR_MIN_LENGTH:
165  //% "The password is too short."
166  return c->qtTrId("cutelyst-valpwq-err-minlength");
167  case PWQ_ERROR_ROTATED:
168  //% "The password is just the rotated old one."
169  return c->qtTrId("cutelyst-valpwq-err-rotated");
170  case PWQ_ERROR_MIN_CLASSES:
171  //% "The password does not contain enough different character types."
172  return c->qtTrId("cutelyst-valpwq-err-minclasses");
173  case PWQ_ERROR_MAX_CONSECUTIVE:
174  //% "The password contains too many same characters consecutively."
175  return c->qtTrId("cutelyst-valpwq-err-maxconsecutive");
176  case PWQ_ERROR_MAX_CLASS_REPEAT:
177  //% "The password contains too many characters of the same type consecutively."
178  return c->qtTrId("cutelyst-valpwq-err-maxclassrepeat");
179  case PWQ_ERROR_MAX_SEQUENCE:
180  //% "The password contains too long a monotonous string."
181  return c->qtTrId("cutelyst-valpwq-err-maxsequence");
182  case PWQ_ERROR_EMPTY_PASSWORD:
183  //% "No password supplied."
184  return c->qtTrId("cutelyst-valpwq-err-emptypw");
185  case PWQ_ERROR_RNG:
186  //% "Password quality check failed because we cannot obtain random "
187  //% "numbers from the RNG device."
188  return c->qtTrId("cutelyst-valpwq-err-rng");
189  case PWQ_ERROR_CRACKLIB_CHECK:
190  //% "The password fails the dictionary check."
191  return c->qtTrId("cutelyst-valpwq-err-cracklibcheck");
192  case PWQ_ERROR_UNKNOWN_SETTING:
193  //% "Password quality check failed because of an unknown setting."
194  return c->qtTrId("cutelyst-valpwq-err-unknownsetting");
195  case PWQ_ERROR_INTEGER:
196  //% "Password quality check failed because of a bad integer value in the settings."
197  return c->qtTrId("cutelyst-valpwq-err-integer");
198  case PWQ_ERROR_NON_INT_SETTING:
199  //% "Password quality check failed because of a settings entry is not "
200  //% "of integer type."
201  return c->qtTrId("cutelyst-valpwq-err-nonintsetting");
202  case PWQ_ERROR_NON_STR_SETTING:
203  //% "Password quality check failed because of a settings entry is not of string type."
204  return c->qtTrId("cutelyst-valpwq-err-nonstrsetting");
205  case PWQ_ERROR_CFGFILE_OPEN:
206  //% "Password quality check failed because opening the configuration file failed."
207  return c->qtTrId("cutelyst-valpwq-err-cfgfileopen");
208  case PWQ_ERROR_CFGFILE_MALFORMED:
209  //% "Password quality check failed because the configuration file is malformed."
210  return c->qtTrId("cutelyst-valpwq-err-cfgfilemalformed");
211  case PWQ_ERROR_FATAL_FAILURE:
212  //% "Password quality check failed because of a fatal failure."
213  return c->qtTrId("cutelyst-valpwq-err-fatalfailure");
214  default:
215  {
216  if (returnValue < 0) {
217  //% "Password quality check failed because of an unknown error."
218  return c->qtTrId("cutelyst-valpwq-err-unknown");
219  } else {
220  if (returnValue < threshold) {
221  //% "The password quality score of %1 is below the threshold of %2."
222  return c->qtTrId("cutelyst-valpwq-err-belowthreshold")
223  .arg(QString::number(returnValue), QString::number(threshold));
224  } else {
225  return {};
226  }
227  }
228  }
229  }
230  } else {
231  switch (returnValue) {
232  case PWQ_ERROR_MEM_ALLOC:
233  //% "Password quality check for the “%1“ field failed because of a "
234  //% "memory allocation error."
235  return c->qtTrId("cutelyst-valpwq-err-memalloc-label").arg(label);
236  case PWQ_ERROR_SAME_PASSWORD:
237  //% "The password in the “%1” field is the same as the old one."
238  return c->qtTrId("cutelyst-valpwq-err-samepass-label").arg(label);
239  case PWQ_ERROR_PALINDROME:
240  //% "The password in the “%1” field is a palindrome."
241  return c->qtTrId("cutelyst-valpwq-err-palindrome-label").arg(label);
242  case PWQ_ERROR_CASE_CHANGES_ONLY:
243  //% "The password in the “%1” field differs with case changes only from the old one."
244  return c->qtTrId("cutelyst-valpwq-err-casechangesonly-label").arg(label);
245  case PWQ_ERROR_TOO_SIMILAR:
246  //% "The password in the “%1” field is too similar to the old one."
247  return c->qtTrId("cutelyst-valpwq-err-toosimilar-label").arg(label);
248  case PWQ_ERROR_USER_CHECK:
249  //% "The password in the “%1” field contains the user name in some form."
250  return c->qtTrId("cutelyst-valpwq-err-usercheck-label").arg(label);
251  case PWQ_ERROR_GECOS_CHECK:
252  //% "The password in the “%1” field contains words from the real name "
253  //% "of the user name in some form."
254  return c->qtTrId("cutelyst-valpwq-err-gecoscheck-label").arg(label);
255  case PWQ_ERROR_BAD_WORDS:
256  //% "The password in the “%1” field contains forbidden words in some form."
257  return c->qtTrId("cutelyst-valpwq-err-badwords-label").arg(label);
258  case PWQ_ERROR_MIN_DIGITS:
259  //% "The password in the “%1” field does not contain enough digits."
260  return c->qtTrId("cutelyst-valpwq-err-mindigits-label").arg(label);
261  case PWQ_ERROR_MIN_UPPERS:
262  //% "The password in the “%1” field does not contain enough uppercase letters."
263  return c->qtTrId("cutelyst-valpwq-err-minuppers-label").arg(label);
264  case PWQ_ERROR_MIN_LOWERS:
265  //% "The password in the “%1” field does not contain enough lowercase letters."
266  return c->qtTrId("cutelyst-valpwq-err-minlowers-label").arg(label);
267  case PWQ_ERROR_MIN_OTHERS:
268  //% "The password in the “%1” field does not contain enough non-alphanumeric "
269  //% "characters."
270  return c->qtTrId("cutelyst-valpwq-err-minothers-label").arg(label);
271  case PWQ_ERROR_MIN_LENGTH:
272  //% "The password in the “%1” field is too short."
273  return c->qtTrId("cutelyst-valpwq-err-minlength-label").arg(label);
274  case PWQ_ERROR_ROTATED:
275  //% "The password in the “%1” field is just the rotated old one."
276  return c->qtTrId("cutelyst-valpwq-err-rotated-label").arg(label);
277  case PWQ_ERROR_MIN_CLASSES:
278  //% "The password in the “%1” field does not contain enough character types."
279  return c->qtTrId("cutelyst-valpwq-err-minclasses-label").arg(label);
280  case PWQ_ERROR_MAX_CONSECUTIVE:
281  //% "The password in the “%1” field contains too many same characters "
282  //% "consecutively."
283  return c->qtTrId("cutelyst-valpwq-err-maxconsecutive-label").arg(label);
284  case PWQ_ERROR_MAX_CLASS_REPEAT:
285  //% "The password in the “%1” field contains too many characters of "
286  //% "the same type consecutively."
287  return c->qtTrId("cutelyst-valpwq-err-maxclassrepeat-label").arg(label);
288  case PWQ_ERROR_MAX_SEQUENCE:
289  //% "The password in the “%1” field contains contains too long a "
290  //% "monotonous string."
291  return c->qtTrId("cutelyst-valpwq-err-maxsequence-label").arg(label);
292  case PWQ_ERROR_EMPTY_PASSWORD:
293  //% "No password supplied in the “%1” field."
294  return c->qtTrId("cutelyst-valpwq-err-emptypw-label").arg(label);
295  case PWQ_ERROR_RNG:
296  //% "Password quality check for the “%1“ field failed because we "
297  //% "cannot obtain random numbers from the RNG device."
298  return c->qtTrId("cutelyst-valpwq-err-rng-label").arg(label);
299  case PWQ_ERROR_CRACKLIB_CHECK:
300  //% "The password in the “%1” field fails the dictionary check."
301  return c->qtTrId("cutelyst-valpwq-err-cracklibcheck-label").arg(label);
302  case PWQ_ERROR_UNKNOWN_SETTING:
303  //% "Password quality check for the “%1“ field failed because of an "
304  //% "unknown setting."
305  return c->qtTrId("cutelyst-valpwq-err-unknownsetting-label").arg(label);
306  case PWQ_ERROR_INTEGER:
307  //% "Password quality check for the “%1“ field failed because of a "
308  //% "bad integer value in the settings."
309  return c->qtTrId("cutelyst-valpwq-err-integer-label").arg(label);
310  case PWQ_ERROR_NON_INT_SETTING:
311  //% "Password quality check for the “%1“ field failed because of a "
312  //% "settings entry is not of integer type."
313  return c->qtTrId("cutelyst-valpwq-err-nonintsetting-label").arg(label);
314  case PWQ_ERROR_NON_STR_SETTING:
315  //% "Password quality check for the “%1“ field failed because of a "
316  //% "settings entry is not of string type."
317  return c->qtTrId("cutelyst-valpwq-err-nonstrsetting-label").arg(label);
318  case PWQ_ERROR_CFGFILE_OPEN:
319  //% "Password quality check for the “%1“ field failed because opening "
320  //% "the configuration file failed."
321  return c->qtTrId("cutelyst-valpwq-err-cfgfileopen-label").arg(label);
322  case PWQ_ERROR_CFGFILE_MALFORMED:
323  //% "Password quality check for the “%1“ field failed because the "
324  //% "configuration file is malformed."
325  return c->qtTrId("cutelyst-valpwq-err-cfgfilemalformed-label").arg(label);
326  case PWQ_ERROR_FATAL_FAILURE:
327  //% "Password quality check for the “%1“ field failed because of a fatal failure."
328  return c->qtTrId("cutelyst-valpwq-err-fatalfailure-label").arg(label);
329  default:
330  {
331  if (returnValue < 0) {
332  //% "Password quality check for the “%1” field failed because of "
333  //% "an unknown error."
334  return c->qtTrId("cutelyst-valpwq-err-unknown-label").arg(label);
335  } else {
336  if (returnValue < threshold) {
337  //% "The quality score of %1 for the password in the “%2” field "
338  //% "is below the threshold of %3."
339  return c->qtTrId("cutelyst-valpwq-err-belowthreshold-label")
340  .arg(QString::number(returnValue), QString::number(threshold));
341  } else {
342  return {};
343  }
344  }
345  }
346  }
347  }
348 }
349 
351 {
352  ValidatorReturnType result;
353 
354  const QString v = value(params);
355 
356  if (!v.isEmpty()) {
357  Q_D(const ValidatorPwQuality);
358  QVariant opts;
359  if (d->options.isValid()) {
360  if (d->options.typeId() == QMetaType::QVariantMap) {
361  opts = d->options;
362  } else if (d->options.typeId() == QMetaType::QString) {
363  const QString optString = d->options.toString();
364  if (c->stash().contains(optString)) {
365  opts = c->stash(optString);
366  } else {
367  opts = d->options;
368  }
369  }
370  }
371  QString un;
372  if (!d->userName.isEmpty()) {
373  un = params.value(d->userName);
374  if (un.isEmpty()) {
375  un = c->stash(d->userName).toString();
376  }
377  }
378  QString opw;
379  if (!d->oldPassword.isEmpty()) {
380  opw = params.value(d->oldPassword);
381  if (opw.isEmpty()) {
382  opw = c->stash(d->oldPassword).toString();
383  }
384  }
385  int rv = validate(v, opts, opw, un);
386  if (rv < d->threshold) {
387  result.errorMessage = validationError(c, rv);
388  if (C_VALIDATOR().isDebugEnabled()) {
389  if (rv < 0) {
390  QList<char> buf(ValidatorPwQualityPrivate::errStrBufSize);
391  qCDebug(C_VALIDATOR).noquote()
392  << debugString(c)
393  << pwquality_strerror(buf.data(), buf.size(), rv, nullptr);
394  } else {
395  qCDebug(C_VALIDATOR).noquote() << debugString(c) << "The quality score" << rv
396  << "is below the threshold of" << d->threshold;
397  }
398  }
399  } else {
400  qCDebug(C_VALIDATOR).noquote()
401  << "ValidatorPwQuality: \"" << v << "\" got a quality score of" << rv;
402  result.value = v;
403  }
404  }
405 
406  return result;
407 }
408 
410 {
411  Q_D(const ValidatorPwQuality);
412  return ValidatorPwQuality::errorString(c, errorData.toInt(), label(c), d->threshold);
413 }
The Cutelyst Context.
Definition: context.h:42
void stash(const QVariantHash &unite)
Definition: context.cpp:562
QString qtTrId(const char *id, int n=-1) const
Definition: context.h:656
Validates an input field with libpwquality to check password quality.
static QString errorString(Context *c, int returnValue, const QString &label=QString(), int threshold=0)
QString genericValidationError(Context *c, const QVariant &errorData) const override
ValidatorPwQuality(const QString &field, int threshold=ValidatorPwQuality::defaultThreshold, const QVariant &options=QVariant(), const QString &userName=QString(), const QString &oldPassword=QString(), const ValidatorMessages &messages=ValidatorMessages())
Base class for all validator rules.
QString validationError(Context *c, const QVariant &errorData={}) const
QString label(Context *c) const
QString value(const ParamsMultiMap &params) const
QString debugString(Context *c) 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 namespace holds all public Cutelyst API.
const char * constData() const const
bool isEmpty() const const
QList::pointer data()
qsizetype size() const const
T value(const Key &key, const T &defaultValue) const const
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QByteArray toUtf8() const const
bool isValid() const const
int toInt(bool *ok) const const
QMap< QString, QVariant > toMap() const const
QString toString() const const
int typeId() const const
Stores custom error messages and the input field label.
Contains the result of a single input parameter validation.
Definition: validatorrule.h:49