cutelyst  3.7.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-2022 Matthias Fehring <mf@huessenbergnetz.de>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include "validatorpwquality_p.h"
7 #include <pwquality.h>
8 #include <QLoggingCategory>
9 
10 using namespace Cutelyst;
11 
12 ValidatorPwQuality::ValidatorPwQuality(const QString &field, int threshold, const QVariant &options, const QString &userName, const QString &oldPassword, const ValidatorMessages &messages) :
13  ValidatorRule(*new ValidatorPwQualityPrivate(field, threshold, options, userName, oldPassword, messages))
14 {
15 
16 }
17 
19 {
20 
21 }
22 
23 int ValidatorPwQuality::validate(const QString &value, const QVariant &options, const QString &oldPassword, const QString &user)
24 {
25  int rv = 0;
26 
27  if (!value.isEmpty()) {
28 
29  pwquality_settings_t *pwq = pwquality_default_settings();
30  if (pwq) {
31 
32  bool optionsSet = false;
33  if (options.isValid()) {
34  if (options.type() == QVariant::Map) {
35  const QVariantMap map = options.toMap();
36  if (!map.empty()) {
37  auto i = map.constBegin();
38  while (i != map.constEnd()) {
39  const QString opt = i.key() + QLatin1Char('=') + i.value().toString();
40  const int orv = pwquality_set_option(pwq, opt.toUtf8().constData());
41  if (orv != 0) {
42  char buf[1024];
43  qCWarning(C_VALIDATOR, "ValidatorPwQuality: Failed to set pwquality option %s: %s", qUtf8Printable(opt), pwquality_strerror(buf, sizeof(buf), orv, nullptr));
44  }
45  ++i;
46  }
47  optionsSet = true;
48  }
49  } else if (options.type() == QVariant::String) {
50  const QString configFile = options.toString();
51  if (!configFile.isEmpty()) {
52  if (C_VALIDATOR().isWarningEnabled()) {
53  void *auxerror;
54  const int rcrv = pwquality_read_config(pwq, configFile.toUtf8().constData(), &auxerror);
55  if (rcrv != 0) {
56  char buf[1024];
57  qCWarning(C_VALIDATOR, "ValidatorPwQuality: Failed to read configuration file %s: %s", qUtf8Printable(configFile), pwquality_strerror(buf, sizeof(buf), rcrv, auxerror));
58  }
59  } else {
60  pwquality_read_config(pwq, configFile.toUtf8().constData(), nullptr);
61  }
62  optionsSet = true;
63  }
64  }
65  }
66 
67  if (!optionsSet) {
68  if (C_VALIDATOR().isWarningEnabled()) {
69  void *auxerror;
70  const int rcrv = pwquality_read_config(pwq, nullptr, &auxerror);
71  if (rcrv != 0) {
72  char buf[1024];
73  qCWarning(C_VALIDATOR, "ValidatorPwQuality: Failed to read default configuration file: %s", pwquality_strerror(buf, sizeof(buf), rcrv, auxerror));
74  }
75  } else {
76  pwquality_read_config(pwq, nullptr, nullptr);
77  }
78  }
79 
80  const QByteArray pwba = value.toUtf8();
81  const char *pw = pwba.constData();
82  const QByteArray opwba = oldPassword.toUtf8();
83  const char *opw = opwba.isEmpty() ? nullptr : opwba.constData();
84  const QByteArray uba = user.toUtf8();
85  const char *u = uba.isEmpty() ? nullptr : uba.constData();
86 
87  rv = pwquality_check(pwq, pw, opw, u, nullptr);
88 
89  pwquality_free_settings(pwq);
90 
91  } else {
92  rv = PWQ_ERROR_MEM_ALLOC;
93  }
94  } else {
95  rv = PWQ_ERROR_EMPTY_PASSWORD;
96  }
97 
98  return rv;
99 }
100 
101 QString ValidatorPwQuality::errorString(Context *c, int returnValue, const QString &label, int threshold)
102 {
103  QString error;
104 
105  if (label.isEmpty()) {
106  switch (returnValue) {
107  case PWQ_ERROR_MEM_ALLOC:
108  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because of a memory allocation error.");
109  break;
110  case PWQ_ERROR_SAME_PASSWORD:
111  error = c->translate("Cutelyst::ValidatorPwQuality", "The password is the same as the old one.");
112  break;
113  case PWQ_ERROR_PALINDROME:
114  error = c->translate("Cutelyst::ValidatorPwQuality", "The password is a palindrome.");
115  break;
116  case PWQ_ERROR_CASE_CHANGES_ONLY:
117  error = c->translate("Cutelyst::ValidatorPwQuality", "The password differs with case changes only.");
118  break;
119  case PWQ_ERROR_TOO_SIMILAR:
120  error = c->translate("Cutelyst::ValidatorPwQuality", "The password is too similar to the old one.");
121  break;
122  case PWQ_ERROR_USER_CHECK:
123  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains the user name in some form.");
124  break;
125  case PWQ_ERROR_GECOS_CHECK:
126  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains words from the real name of the user in some form.");
127  break;
128  case PWQ_ERROR_BAD_WORDS:
129  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains forbidden words in some form.");
130  break;
131  case PWQ_ERROR_MIN_DIGITS:
132  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains too few digits.");
133  break;
134  case PWQ_ERROR_MIN_UPPERS:
135  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains too few uppercase letters.");
136  break;
137  case PWQ_ERROR_MIN_LOWERS:
138  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains too few lowercase letters.");
139  break;
140  case PWQ_ERROR_MIN_OTHERS:
141  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains too few non-alphanumeric characters.");
142  break;
143  case PWQ_ERROR_MIN_LENGTH:
144  error = c->translate("Cutelyst::ValidatorPwQuality", "The password is too short.");
145  break;
146  case PWQ_ERROR_ROTATED:
147  error = c->translate("Cutelyst::ValidatorPwQuality", "The password is just the rotated old one.");
148  break;
149  case PWQ_ERROR_MIN_CLASSES:
150  error = c->translate("Cutelyst::ValidatorPwQuality", "The password does not contain enough different character types.");
151  break;
152  case PWQ_ERROR_MAX_CONSECUTIVE:
153  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains too many same characters consecutively.");
154  break;
155  case PWQ_ERROR_MAX_CLASS_REPEAT:
156  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains too many characters of the same type consecutively.");
157  break;
158  case PWQ_ERROR_MAX_SEQUENCE:
159  error = c->translate("Cutelyst::ValidatorPwQuality", "The password contains too long a monotonous string.");
160  break;
161  case PWQ_ERROR_EMPTY_PASSWORD:
162  error = c->translate("Cutelyst::ValidatorPwQuality", "No password supplied.");
163  break;
164  case PWQ_ERROR_RNG:
165  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because we cannot obtain random numbers from the RNG device.");
166  break;
167  case PWQ_ERROR_CRACKLIB_CHECK:
168  error = c->translate("Cutelyst::ValidatorPwQuality", "The password fails the dictionary check.");
169  break;
170  case PWQ_ERROR_UNKNOWN_SETTING:
171  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because of an unknown setting.");
172  break;
173  case PWQ_ERROR_INTEGER:
174  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because of a bad integer value in the settings.");
175  break;
176  case PWQ_ERROR_NON_INT_SETTING:
177  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because of a settings entry is not of integer type.");
178  break;
179  case PWQ_ERROR_NON_STR_SETTING:
180  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because of a settings entry is not of string type.");
181  break;
182  case PWQ_ERROR_CFGFILE_OPEN:
183  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because opening the configuration file failed.");
184  break;
185  case PWQ_ERROR_CFGFILE_MALFORMED:
186  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because the configuration file is malformed.");
187  break;
188  case PWQ_ERROR_FATAL_FAILURE:
189  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because of a fatal failure.");
190  break;
191  default:
192  {
193  if (returnValue < 0) {
194  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check failed because of an unknown error.");
195  } else {
196  if (returnValue < threshold) {
197  error = c->translate("Cutelyst::ValidatorPwQuality", "The password quality score of %1 is below the threshold of %2.").arg(QString::number(returnValue), QString::number(threshold));
198  }
199  }
200  }
201  break;
202  }
203  } else {
204  switch (returnValue) {
205  case PWQ_ERROR_MEM_ALLOC:
206  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1“ field failed because of a memory allocation error.").arg(label);
207  break;
208  case PWQ_ERROR_SAME_PASSWORD:
209  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field is the same as the old one.").arg(label);
210  break;
211  case PWQ_ERROR_PALINDROME:
212  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field is a palindrome.").arg(label);
213  break;
214  case PWQ_ERROR_CASE_CHANGES_ONLY:
215  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field differs with case changes only.").arg(label);
216  break;
217  case PWQ_ERROR_TOO_SIMILAR:
218  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field is too similar to the old one.").arg(label);
219  break;
220  case PWQ_ERROR_USER_CHECK:
221  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains the user name in some form.").arg(label);
222  break;
223  case PWQ_ERROR_GECOS_CHECK:
224  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains words from the real name of the user name in some form.").arg(label);
225  break;
226  case PWQ_ERROR_BAD_WORDS:
227  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains forbidden words in some form.").arg(label);
228  break;
229  case PWQ_ERROR_MIN_DIGITS:
230  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains too few digits.").arg(label);
231  break;
232  case PWQ_ERROR_MIN_UPPERS:
233  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains too few uppercase letters.").arg(label);
234  break;
235  case PWQ_ERROR_MIN_LOWERS:
236  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains too few lowercase letters.").arg(label);
237  break;
238  case PWQ_ERROR_MIN_OTHERS:
239  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains too few non-alphanumeric characters.").arg(label);
240  break;
241  case PWQ_ERROR_MIN_LENGTH:
242  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field is too short.").arg(label);
243  break;
244  case PWQ_ERROR_ROTATED:
245  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field is just the rotated old one.").arg(label);
246  break;
247  case PWQ_ERROR_MIN_CLASSES:
248  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field does not contain enough character types.").arg(label);
249  break;
250  case PWQ_ERROR_MAX_CONSECUTIVE:
251  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains too many same characters consecutively.").arg(label);
252  break;
253  case PWQ_ERROR_MAX_CLASS_REPEAT:
254  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains too many characters of the same type consecutively.").arg(label);
255  break;
256  case PWQ_ERROR_MAX_SEQUENCE:
257  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field contains contains too long a monotonous string.").arg(label);
258  break;
259  case PWQ_ERROR_EMPTY_PASSWORD:
260  error = c->translate("Cutelyst::ValidatorPwQuality", "No password supplied in the “%1” field.").arg(label);
261  break;
262  case PWQ_ERROR_RNG:
263  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1“ field failed because we cannot obtain random numbers from the RNG device.").arg(label);
264  break;
265  case PWQ_ERROR_CRACKLIB_CHECK:
266  error = c->translate("Cutelyst::ValidatorPwQuality", "The password in the “%1” field fails the dictionary check.").arg(label);
267  break;
268  case PWQ_ERROR_UNKNOWN_SETTING:
269  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1“ field failed because of an unknown setting.").arg(label);
270  break;
271  case PWQ_ERROR_INTEGER:
272  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1“ field failed because of a bad integer value in the settings.").arg(label);
273  break;
274  case PWQ_ERROR_NON_INT_SETTING:
275  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1“ field failed because of a settings entry is not of integer type.").arg(label);
276  break;
277  case PWQ_ERROR_NON_STR_SETTING:
278  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1“ field failed because of a settings entry is not of string type.").arg(label);
279  break;
280  case PWQ_ERROR_CFGFILE_OPEN:
281  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1“ field failed because opening the configuration file failed.").arg(label);
282  break;
283  case PWQ_ERROR_CFGFILE_MALFORMED:
284  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1“ field failed because the configuration file is malformed.").arg(label);
285  break;
286  case PWQ_ERROR_FATAL_FAILURE:
287  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1“ field failed because of a fatal failure.").arg(label);
288  break;
289  default:
290  {
291  if (returnValue < 0) {
292  error = c->translate("Cutelyst::ValidatorPwQuality", "Password quality check for the “%1” field failed because of an unknown error.").arg(label);
293  } else {
294  if (returnValue < threshold) {
295  error = c->translate("Cutelyst::ValidatorPwQuality", "The quality score of %1 for the password in the “%2” field is below the threshold of %3.").arg(QString::number(returnValue), label, QString::number(threshold));
296  }
297  }
298  }
299  break;
300  }
301  }
302 
303  return error;
304 }
305 
307 {
308  ValidatorReturnType result;
309 
310  const QString v = value(params);
311 
312  if (!v.isEmpty()) {
313  Q_D(const ValidatorPwQuality);
314  QVariant opts;
315  if (d->options.isValid()) {
316  if (d->options.type() == QVariant::Map) {
317  opts = d->options;
318  } else if (d->options.type() == QVariant::String) {
319  const QString optString = d->options.toString();
320  if (c->stash().contains(optString)) {
321  opts = c->stash(optString);
322  } else {
323  opts = d->options;
324  }
325  }
326  }
327  QString un;
328  if (!d->userName.isEmpty()) {
329  un = params.value(d->userName);
330  if (un.isEmpty()) {
331  un = c->stash(d->userName).toString();
332  }
333  }
334  QString opw;
335  if (!d->oldPassword.isEmpty()) {
336  opw = params.value(d->oldPassword);
337  if (opw.isEmpty()) {
338  opw = c->stash(d->oldPassword).toString();
339  }
340  }
341  int rv = validate(v, opts, opw, un);
342  if (rv < d->threshold) {
343  result.errorMessage = validationError(c, rv);
344  if (C_VALIDATOR().isDebugEnabled()) {
345  if (rv < 0) {
346  char buf[1024];
347  qCDebug(C_VALIDATOR, "ValidatorPwQuality: Validation failed for field %s at %s::%s: %s", qPrintable(field()), qPrintable(c->controllerName()), qPrintable(c->actionName()), pwquality_strerror(buf, sizeof(buf), rv, nullptr));
348  } else {
349  qCDebug(C_VALIDATOR, "ValidatorPwQuality: Validation failed for field %s at %s::%s because the quality score %i is below the threshold of %i.", qPrintable(field()), qPrintable(c->controllerName()), qPrintable(c->actionName()), rv, d->threshold);
350  }
351  }
352  } else {
353  qCDebug(C_VALIDATOR, "ValidatorPwQuality: \"%s\" got a quality score of %i", qPrintable(v), rv);
354  result.value = v;
355  }
356  }
357 
358  return result;
359 }
360 
362 {
363  QString error;
364 
365  Q_D(const ValidatorPwQuality);
366  const int returnValue = errorData.toInt();
367  const QString _label = label(c);
368  error = ValidatorPwQuality::errorString(c, returnValue, _label, d->threshold);
369 
370  return error;
371 }
The Cutelyst Context.
Definition: context.h:39
void stash(const QVariantHash &unite)
Definition: context.cpp:546
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.
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.
Base class for all validator rules.
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.
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
const char * constData() const const
bool isEmpty() const const
const T value(const Key &key, const T &defaultValue) 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
QVariant::Type type() const const
Stores custom error messages and the input field label.
Contains the result of a single input parameter validation.
Definition: validatorrule.h:49